Servlet:response.setContentLength()减慢了下载速度

Sim*_*mon 15 jsp servlets web-applications http download

private void downloadAllRelease(HttpServletRequest request,
        HttpServletResponse response) {
    LoginToken tok=getToken(request, response);
    int size = 0;
    try {
        ArrayList<Release> releases = manager.getReleases(tok.getUsername);
        ZipOutputStream out = new ZipOutputStream(response.getOutputStream());
        for (int i=0; i<releases.size(); i++) {
            size += releases.get(i).getFile().length;
            out.putNextEntry(new ZipEntry(releases.get(i).getFilename()));
            out.write(releases.get(i).getFile());
            out.closeEntry();
        }
        response.setContentLength(size);
        response.setContentType("application/force-download");
        response.setHeader("Content-Disposition","attachment;filename=release.zip");
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

response.setContentLength()reeeeally减慢下载速度.
如果我不使用它或在out.close()一切仍然正常工作之后使用它,但下载速度会快得多.
有人可以解释我为什么以及是否有必要使用response.setContentLength()

Bal*_*usC 25

也许是因为您指定的大小比实际发送到响应的大,并且webbrowser基本上会混淆并等待更多数据?你知道,ZIP压缩文件并减少最终的大小.

如果您事先无法有效地计算,请不要指定响应的内容长度.无论如何,servlet容器将自动使用分块编码发送它.没错,这会带来更多的开销,并使Web浏览器的下载进度未知,但这并不需要您先在服务器内存中缓冲整个响应,以便获得正确的最终响应内容长度.

如果你真的想要计算最终的响应内容长度,你需要将它全部写入一个ByteArrayOutputStream,然后byte[]通过它的toByteArray()方法得到它.那么真正的响应内容长度就是长度byte[].

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream out = new ZipOutputStream(baos);
// ...

response.setContentLength(baos.size());
response.getOutputStream().write(bytes);
Run Code Online (Sandbox Code Playgroud)

这只是占用更多内存,因为所有内容都将首先存储在服务器的内存中.如果多个用户同时执行此操作并且zip输出相对较大,那么您的服务器可能会迟早会耗尽内存.作为另一种选择,您可以将其FileOutputStream用于创建的临时文件File#createTempFile(),以便您可以通过常规方式获取其大小File#length()并使用FileInputStream它直接将其流式传输到OutputStream响应中.这只是较慢,因为你基本上将字节转移两次.