I am using wildfly-9 with java-8 on windows OS. browser: chrome and firefox For download file implemented Range support. Refer some of the article related range support
- https://github.com/spring-projects/spring-framework/blob/v4.2.0.RC1/spring- webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java#L463
- How to Implement HTTP byte-range requests in Spring MVC
- how to resume an interrupted download
Implemented code fore reference Request from initial servlet
if (httpRequest.getHeader(HttpHeaders.RANGE) == null) {
setHeaders(httpResponse, contentType, contentDisposition, contentEncoding, asrFile.getSize());
writeContent(httpResponse, asrFile.getInputStream());
}
else {
writePartialContent(httpRequest, httpResponse, asrFile.getInputStream(), contentType, contentDisposition,contentEncoding,asrFile.getSize());
}
Implementation for file download to support range
protected void writePartialContent(HttpServletRequest request, HttpServletResponse response,
InputStream in, String contentType, String contentDisposition, String contentEncoding, long contentLength) throws IOException {
long length = contentLength;
List<HttpRange> ranges;
try {
HttpHeaders headers = new ServletServerHttpRequest(request).getHeaders();
ranges = headers.getRange();
}
catch (IllegalArgumentException ex) {
response.addHeader("Content-Range", "bytes */" + length);
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (ranges.size() == 1) {
HttpRange range = ranges.get(0);
long start = range.getRangeStart(length);
long end = range.getRangeEnd(length);
long rangeLength = end - start + 1;
setHeaders(response, contentType,contentDisposition,contentEncoding,contentLength);
response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + length);
response.setContentLength((int) rangeLength);
try {
copyRange(in, response.getOutputStream(), start, end);
}
finally {
try {
in.close();
}
catch (IOException ex) {
log.error("IOException while closing:"+ex,ex);
}
}
}
else {
String boundaryString = MimeTypeUtils.generateMultipartBoundaryString();
response.setContentType("multipart/byteranges; boundary=" + boundaryString);
ServletOutputStream out = response.getOutputStream();
for (HttpRange range : ranges) {
long start = range.getRangeStart(length);
long end = range.getRangeEnd(length);
// Writing MIME header.
out.println();
out.println("--" + boundaryString);
if (contentType != null) {
out.println("Content-Type: " + contentType);
}
out.println("Content-Range: bytes " + start + "-" + end + "/" + length);
out.println();
// Printing content
copyRange(in, out, start, end);
}
out.println();
out.print("--" + boundaryString + "--");
}
}
private void copyRange(InputStream in, OutputStream out, long start, long end) throws IOException {
long skipped = in.skip(start);
if (skipped < start) {
throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required.");
}
long bytesToCopy = end - start + 1;
byte buffer[] = new byte[StreamUtils.BUFFER_SIZE];
while (bytesToCopy > 0) {
int bytesRead = in.read(buffer);
if (bytesRead <= bytesToCopy) {
out.write(buffer, 0, bytesRead);
bytesToCopy -= bytesRead;
}
else {
out.write(buffer, 0, (int) bytesToCopy);
bytesToCopy = 0;
}
if (bytesRead < buffer.length) {
break;
}
}
}
protected void setHeaders(HttpServletResponse response, String contentType, String contentDisposition, String contentEncoding, long contentLength) throws IOException {
response.setContentLength((int) contentLength);
if (contentType != null) {
response.setContentType(contentType);
}
if(contentEncoding != null) {
response.setHeader("Content-Encoding", contentEncoding);
}
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
response.setHeader("Content-Disposition", contentDisposition);
//note: setting unique value per download file
response.setHeader("ETag", ETag);
}
protected void writeContent(HttpServletResponse response, InputStream in) throws IOException {
try {
try {
StreamUtils.copy(in, response.getOutputStream());
}
finally {
try {
in.close();
}
catch (Throwable ex) {
log.error("Throwable while closing:"+ex,ex);
}
}
}
catch (FileNotFoundException ex) {
log.error("FileNotFoundException while closing:"+ex,ex);
}
}
to check its working or not I am shutdown server in my system & notice some of bytes are corrupted in downloaded file.
1st line with junk character
2nd line was original content
Apache Log details
+----+------------+------+-------------------------------+-----------------------------------------+-------------------+------------------------+-----------------------------------------+ | No | Host | - - | Date&Time | "Request details" | HTTP status code | content lenght(bytes) | Description | +----+------------+------+-------------------------------+-----------------------------------------+-------------------+------------------------+-----------------------------------------+ | 1 | 127.0.0.1 | - - | [07/Nov/2022:18:41:06 +0530] | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 502 | 15466666 | (when server is stopped) | | 2 | 127.0.0.1 | - - | [07/Nov/2022:18:42:14 +0530] | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 503 | 323 | (after server is stopped) | | 3 | 127.0.0.1 | - - | [07/Nov/2022:18:48:14 +0530] | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 206 | 27905015 | (after re-start server partial content) | +----+------------+------+-------------------------------+-----------------------------------------+-------------------+------------------------+-----------------------------------------+
I have doubt on second request, however content size is not match (323) with that junk char size.
I have also check with Download manager(single thread)
Apache logs
+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+---------------------------------+ | No | Host | Date&Time | "Request details" | HTTP status code | content lenght(bytes) | Description | +----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+---------------------------------+ | 1 | 127.0.0.1 | [07/Nov/2022:19:00:49 +0530] | "HEAD /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 502 | - | Initial request | | 2 | 127.0.0.1 | [07/Nov/2022:19:01:35 +0530] | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 503 | 323 | Once server is shutdown | | 3 | 127.0.0.1 | [07/Nov/2022:19:05:18 +0530] | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 200 | 40952 | Once server re-start done | | 4 | 127.0.0.1 | [07/Nov/2022:19:05:44 +0530] | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 206 | 33672353 | another partial content request | +----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+---------------------------------+
Seems like below request is served by apache when server is shutdown(offline)
+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+-------------------------+ | No | Host | Date&Time | "Request details" | HTTP status code | content lenght(bytes) | Description | +----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+-------------------------+ | 2 | 127.0.0.1 | [07/Nov/2022:19:01:35 +0530] | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" | 503 | 323 | Once server is shutdown | +----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+-------------------------+
Is there any configuration to avoid these junk characters/data or any details so I can troubleshoot this issue further.