I am developing a Java 8 servlet to download a zipped resource. The file size can vary significantly, sometimes reaching up to 700MB. For testing, I am using a 350MB ZIP file.
The expected behavior is that the file should start downloading immediately while streaming, but instead, the browser buffers the file before prompting the user to save it. This results in a delay of 30-40 seconds before the save dialog appears.
My Servlet Code:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String strCryptedURL = request.getParameter(DownloadFolderServlet.URL_PARAM_NAME);
String strURL = PasswordUtil.isPasswordEncrypted(strCryptedURL) ? PasswordUtil.decryptPassword(strCryptedURL) : strCryptedURL;
try {
URL url = new URL(strURL);
String userInfo = url.getUserInfo();
if (userInfo == null || !userInfo.contains(":")) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing credentials for WebDAV.");
return;
}
String[] userPass = userInfo.split(":", 2);
String username = userPass[0];
String password = userPass[1];
String sanitizedURL = url.getProtocol() + "://" + url.getHost() + ":" + url.getPort() + url.getPath();
HttpURLConnection fileConn = (HttpURLConnection) new URL(sanitizedURL).openConnection();
fileConn.setRequestMethod("GET");
fileConn.setConnectTimeout(10000);
fileConn.setReadTimeout(60000);
String encodedAuth = Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
fileConn.setRequestProperty("Authorization", "Basic " + encodedAuth);
if (fileConn.getResponseCode() != HttpURLConnection.HTTP_OK) {
response.sendError(fileConn.getResponseCode(), "Error retrieving file from WebDAV.");
return;
}
String fileName = url.getPath().substring(url.getPath().lastIndexOf("/") + 1);
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Transfer-Encoding", "chunked"); // Tried enabling chunked encoding
response.flushBuffer();
try (InputStream inputStream = fileConn.getInputStream();
OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[8192]; // 8 KB buffer
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
outputStream.flush(); // Ensuring data is sent immediately
}
}
} catch (Exception e) {
e.printStackTrace();
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error while downloading the ZIP file.");
}
}
I have tried:
- Setting Transfer-Encoding: chunked
- Ensuring Content-Type: application/octet-stream
- Using flushBuffer()
- Explicitly avoiding Content-Length (to prevent buffering)
Expected Behavior: The browser should prompt the user immediately and start downloading progressively instead of waiting for the entire file to be received.
Questions:
- Is there anything in my implementation that causes the browser to buffer everything before showing the save prompt?
- Is there a way to force the browser to stream the file as soon as bytes are available?