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()吗?
response.setContentLength()
out.close()
也许是因为您指定的大小大于实际发送给响应的大小,并且Web浏览器基本上变得混乱并且正在等待更多数据?您知道,ZIP压缩文件并减小最终大小。
如果您不能事先有效地计算出响应的内容长度,那就不要指定它。不管怎样,servlet容器将自动以分块编码发送它。没错,这会增加一些开销,并使Web浏览器的下载进度未知,但这并不需要您先将整个响应缓存在服务器的内存中,这样您才能获得适当的最终响应内容长度。
如果您 真的 想计算最终响应内容的长度,则需要将其全部写入a ByteArrayOutputStream,然后byte[]通过其toByteArray()方法获取。那么实际的响应内容长度就是的长度byte[]。
ByteArrayOutputStream
byte[]
toByteArray()
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以通常的方式将其直接流式传输到响应中。这只会变慢,因为您基本上是在两次传输字节。
FileOutputStream
File#createTempFile()
File#length()
FileInputStream
OutputStream