Servlet 3.0 Tutorial: Uploading files
The Internet is becoming more and more about sharing data, and, uploading files had become nearly universal requirement for a web application. Prior to Servlet 3.0, implementing file upload successfully required external libraries or tedious input processing. Version 3.0 of the spec goes a long way towards providing us with a solution to the problem in a generic and portable way.
Servlet 3.0 introduces a new API to handle multipart/form-data submissions. Multipart/form-data is a content type used to submit files, non-ASCII and binary data.
The browser will submit data using multipart/form-data encoding when an HTML form specifies it as a value for its enctype attribute.
Form example:
<FORM action="/multipart-upload"
enctype="multipart/form-data"
method="POST">
Submitter: <INPUT type="text" name="submitter">
Upload File: <INPUT type="file" name="content">
<INPUT type="submit" value="Submit">
</FORM>
When submitting a form, the browser will stream the content in, all parts combined, with each part representing a field of a form. Parts are named after the input elements and separated from each other with string delimiters named boundary. For easier visualization I’ll show what submitted data would look like:
POST /multipart-upload HTTP/1.0 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryroNMZtQ4wMWb0fgH Content-Length: 1000 ------WebKitFormBoundaryroNMZtQ4wMWb0fgH Content-Disposition: form-data; name="submitter" John Doe ------WebKitFormBoundaryroNMZtQ4wMWb0fgH Content-Disposition: form-data; name="content"; filename="a.txt" Content-Type: text/plain Some Data
The new Servlet 3.0 API specifies an interface javax.servlet.http.Part for each submission part and javax.servlet.http.HttpServletRequest makes them accessible using two methods:
- getParts()
- getPart(String name)
Since parts are named, method getPart(String name) can be used to access a particular part. Alternatively, method getParts() which returns an Iterable<Part> can be used to get an Iterator over all the parts.
javax.servlet.http.Part is a really simple interface which provides methods allowing to introspect each part and get its name, size, content-type, query the headers submitted with a part, delete or write part out to a disk. The interface has a few attributes and it may be useful to show it inline in its entirety.
package javax.servlet.http;
public interface Part {
public InputStream getInputStream()
throws IOException;
public String getContentType();
public String getName();
public long getSize();
public void write(String fileName)
throws IOException;
public void delete()
throws IOException;
public String getHeader(String name);
public Iterable<String> getHeaders(String name);
public Iterable<String> getHeaderNames();
}
So a simple servlet that accepts and saves a file could look as following:
package qa;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class MyServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//get the "content" part and save it.
req.getPart("content").write("/tmp/file.dat");
}
}
Before Resin 4.0.7 methods getPart(String) and getParts() return null in default configuration. Add <multipart-form enable=”true”/> tag to WEB-INF/resin-web.xml to turn the new Servlet 3.0 feature on.
<web-app xmlns="http://caucho.com/ns/resin"> <multipart-form enable="true"/> </web-app>
A special explanation should be given to method delete(). When invoked the method will delete the underlying temporary file as well as the file that the data has been moved or copied to using method write(String), thus method delete() should only be used if it’s necessary to delete the temporary file. In this situation method write(…) would have been never called in the course of processing.
Note: Resin keeps track of temporary files associated with a request and deletes them when it completes serving the request.
@MultipartConfig annotation
The spec introduces a new @javax.servlet.annotation.MultipartConfig annotation that helps the container identify a Servlet as capable of handling multipart/form-data requests. When this annotation is present on a Servlet, the HttpServletRequest makes the parts available.
The annotation can be used to configure location where the container should store temporary files, legal size limits on the entire request and parts, and a threshold size that triggers use of permanent storage.
@MultipartConfig annotation declares the following attributes:
- location
- maxFileSize
- maxRequestSize
- fileSizeThreshold
Resin’s implementation for Part’s write(…) method attempts to move data instead of copying. As moving files requires source and target be on the same partition co-location of temporary files and target files may offer superior performance. If the data can not be moved it is copied.
Example below illustrates use of @MultipartConfig annotation.
package qa;
import java.io.*;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.*;
import javax.servlet.http.*;
@MultipartConfig(location="/data")
public class MyServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//target directory and location are the same. The file is moved(not copied)
req.getPart("content").write("/data/file.dat");
}
}
Servlet 3.0 offers more interesting features and I will try to cover support for Asynchronous communication next.
Tags: multipart/form-data, MultipartConfig, Servlet 3.0, tutorial

April 26th, 2010 at 12:35 pm
Beautiful tutorial. Waiting for the Asynchronous communication tutorial