I have a servlet in embedded Jetty that provides downloads for certain files.
The code I use is the following:
try{
File file = new File(filePath);
response.setContentLengthLong(file.length());
response.setHeader("Content-Disposition", "attachment; filename=myfilename.mkv");
response.setContentType(Files.probeContentType(file.toPath()));
response.setBufferSize(5242880);
in = new FileInputStream(file);
out = response.getOutputStream();
byte[] bytes = new byte[5242880];
int bytesRead;
while ((bytesRead = in.read(bytes)) != -1) {
try {
out.write(bytes, 0, bytesRead);
} catch (EOFException e) {
logger.debug("Reached end of file, breaking loop");
break;
}
}
} catch ( Exception e ) {
e.printStackTrace();
}
For some reason, when I access this servlet from Chrome, the download starts, but I do not see the download percentage nor the total size of the file, and when I check the response headers of the request, Content-Length
is not there, but Transfer-Encoding: Chunked
is.
I have tried removing the Content-Disposition
header, but the result is the same.
I also tried to set a fake content length header instead of file.length()
and set it to response.setContentLengthLong(10000);
(the file is several GB). The download stops after those 10k bytes.
Another note, while debugging, the response object does have the content length header, but for some reason it is deleted or overwritten automatically.
What could the problem be?
Update after adding response.flushBuffer():
String filePath = "path/to/file";
try {
// Starting the actual stream. It will handle videos differently from other files, in order to support streaming
Path file = Paths.get(filePath);
response.setContentLengthLong(Files.size(file));
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileEntity.fileName + fileEntity.extension + "\"");
response.setContentType(Files.probeContentType(file));
response.flushBuffer();
final int bufferSize = response.getBufferSize();
try (InputStream in = Files.newInputStream(file)) {
ServletOutputStream out = response.getOutputStream();
byte[] bytes = new byte[bufferSize];
int bytesRead;
while ((bytesRead = in.read(bytes)) != -1) {
out.write(bytes, 0, bytesRead);
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>11.0.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>11.0.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<version>11.0.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>11.0.6</version>
</dependency>
In this case the problem was not jetty or any part of the code, but it was in the apache virtualhost configuration. After i removed the following lines it started sending the Content-Length header as expected:
SetOutputFilter DEFLATE
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \
\.(?:mp3|wav|wma|au|m4p|snd|mid|wmv|mpg|mpeg|mp4|qt|mov)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \.pdf$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \
\.(?:exe|t?gz|zip|gz2|sit|rar)$ no-gzip dont-vary