一尘不染

Servlet:response.setContentLength()会降低下载速度

jsp

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()吗?


阅读 1692

收藏
2020-06-08

共1个答案

一尘不染

也许是因为您指定的大小大于实际发送给响应的大小,并且Web浏览器基本上变得混乱并且正在等待更多数据?您知道,ZIP压缩文件并减小最终大小。

如果您不能事先有效地计算出响应的内容长度,那就不要指定它。不管怎样,servlet容器将自动分块编码发送它。没错,这会增加一些开销,并使Web浏览器的下载进度未知,但这并不需要您先将整个响应缓存在服务器的内存中,这样您才能获得适当的最终响应内容长度。

如果您 真的 想计算最终响应内容的长度,则需要将其全部写入a
ByteArrayOutputStream,然后byte[]通过其toByteArray()方法获取。那么实际的响应内容长度就是的长度byte[]

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

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

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

2020-06-08