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()
reeeeally slows downloads down.
If I don't use It or put it after out.close()
everything still works fine but downloads are muuuuch faster.
Can someone explain me why and if it is necessary to use response.setContentLength()
?
According to the documentation, the setContentLength(int len) method of the ServletResponse interface sets the length of the content body in the response In HTTP servlets, this method sets the HTTP Content-Length header.
public abstract interface HttpServletResponse extends ServletResponse. Defines an HTTP servlet response that a servlet running on a Web server sends to a client using HTTP. This interface allows the servlet's service method to access HTTP headers and return data to its client.
Perhaps because you're specifying a larger size than what is actually been sent to the response and the webbrowser basically get confused and is waiting for more data? You know, ZIP compresses files and reduces the final size.
Just don't specify the response's content length if you cannot efficiently calculate it beforehand. The servlet container will automatically send it with chunked encoding anyway. True, this has a little more overhead and leaves the webbrowser with an unknown download progress, but this doesn't require you to buffer the entire response in server's memory first so that you can get the proper final response content length.
If you really want to calculate the final response content length, you'd need to write it all to a ByteArrayOutputStream
instead and then get the byte[]
by its toByteArray()
method. The real response content length is then the length of the byte[]
.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream out = new ZipOutputStream(baos);
// ...
response.setContentLength(baos.size());
response.getOutputStream().write(bytes);
This is only more memory hogging because everything will be stored in server's memory first. If multiple users do this concurrently and the zip output is relatively large, then your server might risk to run out of memory sooner or later. As another alternative, you could write it using FileOutputStream
to a temp file as created by File#createTempFile()
, so that you can obtain its size by File#length()
and use FileInputStream
to stream it directly into the OutputStream
of the response the usual way. This is only slower as you're basically transferring the bytes around twice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With