使用ServletOutputStream在Java servlet中编写非常大的文件而不会出现内存问题

Mar*_*tin 39 java websphere servlets stream

我正在使用IBM Websphere Application Server v6和Java 1.4,并且正在尝试将大型CSV文件写入ServletOutputStream供用户下载.目前文件范围为50-750MB.

较小的文件不会导致太多问题,但是对于较大的文件,它似乎被写入堆中,然后导致OutOfMemory错误并关闭整个服务器.

这些文件只能通过HTTPS提供给经过身份验证的用户,这就是我通过Servlet服务它们而不是仅仅将它们粘贴在Apache中的原因.

我正在使用的代码是(在此周围删除了一些绒毛):

    resp.setHeader("Content-length", "" + fileLength);
    resp.setContentType("application/vnd.ms-excel");
    resp.setHeader("Content-Disposition","attachment; filename=\"export.csv\"");

    FileInputStream inputStream = null;

    try
    {
        inputStream = new FileInputStream(path);
        byte[] buffer = new byte[1024];
        int bytesRead = 0;

        do
        {
            bytesRead = inputStream.read(buffer, offset, buffer.length);
            resp.getOutputStream().write(buffer, 0, bytesRead);
        }
        while (bytesRead == buffer.length);

        resp.getOutputStream().flush();
    }
    finally
    {
        if(inputStream != null)
            inputStream.close();
    }
Run Code Online (Sandbox Code Playgroud)

FileInputStream似乎没有,如果我写到另一个文件或只是删除完全写入内存使用情况似乎并不成为一个问题而导致问题.

我的想法是resp.getOutputStream().write存储在内存中,直到数据可以发送到客户端.因此整个文件可能会被读取并存储在resp.getOutputStream()导致我的内存问题和崩溃的位置!

我已经尝试过缓冲这些流,并尝试使用Channels java.nio,其中任何一个似乎都没有对我的内存问题产生任何影响.我还在OutputStream每次迭代循环和循环之后刷新了一次,这没有帮助.

Bal*_*usC 43

平均体面的servlet容器本身默认每次刷新流量大约2KB.你真的应该没有需要再调用flush()OutputStreamHttpServletResponse间隔顺序时,从一个相同的源流数据.在例如Tomcat(和Websphere!)中,这可以配置为bufferSizeHTTP连接器的属性.

如果内容长度事先未知(根据Servlet API规范!)并且客户端支持HTTP 1.1,那么平均体面的servlet容器也只是以的形式流式传输数据.

问题症状至少表明servletcontainer在刷新之前缓冲内存中的整个流.这可能意味着未设置内容长度标头和/或servlet容器不支持分块编码和/或客户端不支持分块编码(即它使用HTTP 1.0).

要修复其中一个,只需预先设置内容长度:

response.setHeader("Content-Length", String.valueOf(new File(path).length()));
Run Code Online (Sandbox Code Playgroud)