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

时间:2012-01-10 00:20:57

标签: http jsp web-applications servlets 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();
    }
}

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

1 个答案:

答案 0 :(得分:22)

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

如果您事先无法有效地计算,请不要指定响应的内容长度。无论如何,servlet容器将automaticallychunked encoding发送它。没错,这会产生更多的开销,并使Web浏览器的下载进度不明,但这并不需要先在服务器内存中缓冲整个响应,以便获得正确的最终响应内容长度。

如果确实想要计算最终回复内容的长度,则需要将其全部写入ByteArrayOutputStream,然后通过{byte[]获取toByteArray() {1}}方法。实际响应内容长度是byte[]的长度。

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

response.setContentLength(baos.size());
response.getOutputStream().write(bytes);

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