I discovered a few days back an issue with our product on HTTP downloads > 2GB. It appears to be a simple overflow on the HttpServletResponse.setContentLength method. You can probably excuse the API designers circa 1997 assuming a 32-bit signed Integer with max value 231-1 (2147483647 bytes) would be sufficient. The Gigabit ethernet standard did not come for another year (1998)!
Here is the exception seen when you provide a long value greater than 2147483647 bytes to the setContentLength(int) method:
java.net.ProtocolException: Exceeded stated content-length of: '-XXXX' bytes
at weblogic.servlet.internal.ServletOutputStreamImpl.checkCL(ServletOutputStreamImpl.java:200)
Below is a sample download servlet with workaround for the 2gb limitation. It has been tested on Firefox 3.6 against WebLogic Server 10.3.6 with a 2.2GB download and worked perfectly.
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet
extends HttpServlet
{
@SuppressWarnings("compatibility:1533750721037291976")
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
doPost(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// if no file parameter specified, download hosts file
String file = request.getParameter("file");
file = (file == null || file.length() == 0) ? "/etc/hosts" : file;
File fileObj = new File(file);
if ((!fileObj.exists()) || (!fileObj.isFile()) || (!fileObj.canRead()))
{
throw new IOException("'file' '" + file + "' cannot be read.");
}
ServletContext context = getServletConfig().getServletContext();
String mimetype = context.getMimeType(file);
response.setContentType(mimetype == null ? "application/octet-stream" :
mimetype);
long length = fileObj.length();
if (length <= Integer.MAX_VALUE)
{
response.setContentLength((int)length);
}
else
{
response.addHeader("Content-Length", Long.toString(length));
}
response.setHeader("Content-Disposition",
"attachment; filename=\"" + fileObj.getName() + "\"");
ServletOutputStream out = response.getOutputStream();
InputStream in = null;
byte[] buffer = new byte[32768];
try
{
in = new FileInputStream(fileObj);
int bytesRead;
while ((bytesRead = in.read(buffer)) >= 0)
{
out.write(buffer, 0, bytesRead);
}
}
finally
{
if (in != null)
{
in.close();
}
}
}
}
No comments:
Post a Comment