我有一个Web服务器项目,尝试下载大文件时出现异常。通过流将文件读取并写入ServletOutputStream。
样例代码:
private void readFromInput(BufferedInputStream fis, ServletOutputStream sout) throws IOException { byte[] buf = new byte[4096]; int c = 0; while ((c = fis.read(buf)) != -1) { sout.write(buf, 0, c); } fis.close(); }
当我查看回溯时,我看到执行了一些过滤器。
这是异常的某些部分:
javax.servlet.ServletException: #{DownloaderBean.actionDownload}: java.lang.OutOfMemoryError: Java heap space javax.faces.webapp.FacesServlet.service(FacesServlet.java:256) org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:144) org.ajax4jsf.framework.ajax.xmlfilter.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:127) org.ajax4jsf.framework.ajax.xmlfilter.BaseFilter.doFilter(BaseFilter.java:277) .... .... .... java.lang.OutOfMemoryError: Java heap space java.io.ByteArrayOutputStream.write(Unknown Source) org.apache.myfaces.webapp.filter.ExtensionsResponseWrapper$MyServletOutputStream.write(ExtensionsResponseWrapper.java:135)
当我看一下ExtensionFilter代码时:
http://grepcode.com/file/repo1.maven.org/maven2/org.apache.myfaces.tomahawk/tomahawk12/1.1.7/org/apache/myfaces/webapp/filter/ExtensionsFilter.java
此页面上有一部分:
"When the ExtensionsFilter is enabled, and the DefaultAddResources implementation is used then there is no way to avoid having the response buffered in memory"
我猜这些过滤器会缓冲堆上的响应并导致问题。有没有办法防止此过滤器应用于特殊页面/链接?还是我应该遵循另一种方式来处理此问题?
MyFaces ExtensionsFilter显然将_整个_响应缓冲在服务器的内存中,直到最后一位为止。因此,您基本上有2个选择:
ExtensionsFilter
摆脱MyFaces ExtensionsFilter。
不要让请求点击MyFaces ExtensionsFilter。
如果您实际上需要Web应用程序中的某些功能要求,那么选项1可能会非常激烈,但是如果可以找到替代方法,则可行。例如,如果您仅需要它来处理文件上传,则可以考虑为此使用替代组件库,甚至是标准JSF 2.2。
选项2可以通过两种方式实现:
<filter-mapping>
FacesServlet
例如,当仅应调用它时/upload.jsf,请替换<servlet-name>为<url-pattern>:
/upload.jsf
<servlet-name>
<url-pattern>
<filter-mapping> <filter-name>MyFacesExtensionsFilter</filter-name> <url-pattern>/upload.jsf</url-pattern> </filter-mapping>
仅当您实际上从同一页面执行下载操作时,这才很麻烦。
例如
@WebServlet("/files/*") public class FileServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String filename = request.getPathInfo().substring(1); // Just do your job to get the File or InputStream, depending on the functional requirements. // This kickoff example just allocates a file in the file system. File file = new File("/path/to/files", filename); response.setHeader("Content-Type", getServletContext().getMimetype(filename)); response.setHeader("Content-Length", String.valueOf(file.length())); response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\""); Files.copy(file.toPath(), response.getOutputStream()); } }
(注意:如果您仍未使用Servlet 3.0,请替换@WebServlet为;中的常规Servlet映射web.xml;如果您仍不在Java 7上,请替换Files#copy()为常规的InputStream/ OutputStream循环样板)
@WebServlet
web.xml
Files#copy()
InputStream
OutputStream
调用它的方式如下(假设您已链接到JSF1.2的Tomahawk的源代码;因此不支持模板文本中的EL,那么请假设在JSP上使用旧版JSF 1.2)。
<h:outputLink value="#{request.contextPath}/files/#{bean.filename}"> <h:outputText value="Download #{bean.filename}" /> </h:outputLink>
如果下载需要其他参数,请使用传递它们<f:param>:
<f:param>
<h:outputLink value="#{request.contextPath}/files/#{bean.filename}"> <f:param name="foo" value="#{bean.foo}" /> <f:param name="bar" value="#{bean.bar}" /> <h:outputText value="Download #{bean.filename}" /> </h:outputLink>
然后可以在servlet中获得,如下所示:
String foo = request.getParameter("foo"); String bar = request.getParameter("bar"); // ...