Streaming Large Files in a Java Servlet

Streaming large files in a java servlet

When possible, you should not store the entire contents of a file to be served in memory. Instead, aquire an InputStream for the data, and copy the data to the Servlet OutputStream in pieces. For example:

ServletOutputStream out = response.getOutputStream();
InputStream in = [ code to get source input stream ];
String mimeType = [ code to get mimetype of data to be served ];
byte[] bytes = new byte[FILEBUFFERSIZE];
int bytesRead;

response.setContentType(mimeType);

while ((bytesRead = in.read(bytes)) != -1) {
out.write(bytes, 0, bytesRead);
}

// do the following in a finally block:
in.close();
out.close();

I do agree with toby, you should instead "point them to the S3 url."

As for the OOM exception, are you sure it has to do with serving the image data? Let's say your JVM has 256MB of "extra" memory to use for serving image data. With Google's help, "256MB / 200KB" = 1310. For 2GB "extra" memory (these days a very reasonable amount) over 10,000 simultaneous clients could be supported. Even so, 1300 simultaneous clients is a pretty large number. Is this the type of load you experienced? If not, you may need to look elsewhere for the cause of the OOM exception.

Edit - Regarding:

In this use case the images can contain sensitive data...

When I read through the S3 documentation a few weeks ago, I noticed that you can generate time-expiring keys that can be attached to S3 URLs. So, you would not have to open up the files on S3 to the public. My understanding of the technique is:

  1. Initial HTML page has download links to your webapp
  2. User clicks on a download link
  3. Your webapp generates an S3 URL that includes a key that expires in, lets say, 5 minutes.
  4. Send an HTTP redirect to the client with the URL from step 3.
  5. The user downloads the file from S3. This works even if the download takes more than 5 minutes - once a download starts it can continue through completion.

Using ServletOutputStream to write very large files in a Java servlet without memory issues

The average decent servletcontainer itself flushes the stream by default every ~2KB. You should really not have the need to explicitly call flush() on the OutputStream of the HttpServletResponse at intervals when sequentially streaming data from the one and same source. In for example Tomcat (and Websphere!) this is configureable as bufferSize attribute of the HTTP connector.

The average decent servletcontainer also just streams the data in chunks if the content length is unknown beforehand (as per the Servlet API specification!) and if the client supports HTTP 1.1.

The problem symptoms at least indicate that the servletcontainer is buffering the entire stream in memory before flushing. This can mean that the content length header is not set and/or the servletcontainer does not support chunked encoding and/or the client side does not support chunked encoding (i.e. it is using HTTP 1.0).

To fix the one or other, just set the content length beforehand:

response.setContentLengthLong(new File(path).length());

Or when you're not on Servlet 3.1 yet:

response.setHeader("Content-Length", String.valueOf(new File(path).length()));

Java servlet not able to recieve large files

From the message it seems that the connection is closed by either client or server or some proxy in the middle.

Connections are usually closed when timeout occurs.

While normally I would not expect timeout to occur during the file upload, it still can occur in case your server is having memory problems and GC is working hard. So I suggest to monitor the GC times during the file upload. If the are high, you are having memory problem and should not store the whole content of file in memory.

How to stream several large files to jetty

A request with multipart/formdata is processed by various internal components to break apart the sections so that HttpServletRequest.getParts() (and various similar methods) can work properly.

Option #1: Handle Multi-part yourself

It can be a bit tricky to subvert this behavior of the Servlet spec, but I'll give it a go.

First, do not declare the @MultipartConfig configuration for the servlet you want to handle this request data.

Next, do not access methods in the HttpServletRequest that need to know about the parameters of the request, or its parts.

Override the HttpServlet.service(HttpServletRequest, HttpServletResponse) method, not the doPost() method, and process the raw Request payload content yourself.

This means you'll be writing a MultiPart InputStream Parser, and handling the parsing of the multi-part yourself. There's plenty of examples of this online, you'll just want to pick one that makes more sense to you.

Option #2: Don't use POST with multi-part

If you are streaming upload a file, don't use POST with multi-part, use PUT with raw payload data, then you'll skip the entire layer of magic that is the multi-part request POST payload.

Can't Download large files in html request

Every time flush the output stream object in the while loop after reading some data. you can set a long value and check. if that limit reaches you can flush the data in the output stream object, so that system will flushes that much amount of data and free the memory allocated for that so will not come the out of memory error.

ServletOutputStream sos = response.getOutputStream();
long byteRead = 0;
try {
byte[] buf = new byte[8291];
while (true) {
int r = is.read(buf);
if (r == -1)
break;
sos.write(buf, 0, r);
byteRead +=r;
if(byteRead > 1024*1024){ //flushes after 1mb
byteRead = 0;
sos.flush();
}

}
} finally {
if(sos != null){
sos.flush();
}
try{is.close();}catch(Exception e){}
try{sos.close();}catch(Exception e){}
}

Prevent client timing out while a servlet generates a large download

Well, it looks like shrinking the size of my output buffer with response.setBufferSize(1000); allowed my stress test file to download successfully. I still don't know why response.flushBuffer() didn't seem to do anything, but at least as long as I generate data quickly enough to fill that buffer size before timing out, the download will complete.

How to send a large file over Http Servlet response

App Engine has quotas, designed to protect everyone from runaway applications. The maximum amount of data your application can receive in a single inbound HTTP request is 32MB.

If you want to upload larger files then you should look at Blobstore API.

See the quota documentation for more details.



Related Topics



Leave a reply



Submit