Introduction
To browse and select a file for upload you need a HTML
<input type="file"> field in the form. As stated in the
HTML specification you have to use the
POST
method and the enctype attribute of the form has to be set to
"multipart/form-data".
<form action="upload" method="post" enctype="multipart/form-data">
<input type="text" name="description" />
<input type="file" name="file" />
<input type="submit" />
</form>
After submitting such a form, the binary multipart form data is available in the request body in
a different format than when the
enctype isn't set.
Before Servlet 3.0, the Servlet API didn't natively support
multipart/form-data. It supports only the default form enctype of
application/x-www-form-urlencoded. The
request.getParameter() and consorts would all return
null
when using multipart form data.
Don't manually parse it!
You can in theory parse the request body yourself based on
ServletRequest#getInputStream(). However, this is a precise and tedious work which requires precise knowledge of
RFC2388. You shouldn't try to do this on your own or copypaste some homegrown library-less code found elsewhere on the Internet. Many online sources have failed hard in this, such as roseindia.net. You should rather use a real library which is used (and implicitly tested!) by millions of users for years. Such a library has proven its robustness.
When you're not on Servlet 3.0 yet, use Apache Commons FileUpload
If you're not on Servlet 3.0 yet, the common practice is to make use of
Apache Commons FileUpload to parse the multpart form data requests. It has an excellent
User Guide and
FAQ (carefully go through both). There's also the O'Reilly ("cos")
MultipartRequest, but it has some (minor) bugs and isn't actively maintained anymore for years. I wouldn't recommend using it. Apache Commons FileUpload is still actively maintained and currently very mature.
In order to use Apache Commons FileUpload, you need to have at least the following files in your webapp's
/WEB-INF/lib:
Your initial attempt failed most likely because you forgot the commons IO.
Here's a kickoff example how the doPost() of your UploadServlet may look like when using Apache Commons FileUpload:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
for (FileItem item : items) {
if (item.isFormField()) {
// Process regular form field (input type="text|radio|checkbox|etc", select, etc).
String fieldname = item.getFieldName();
String fieldvalue = item.getString();
// ... (do your job here)
} else {
// Process form file field (input type="file").
String fieldname = item.getFieldName();
String filename = FilenameUtils.getName(item.getName());
InputStream filecontent = item.getInputStream();
// ... (do your job here)
}
}
} catch (FileUploadException e) {
throw new ServletException("Cannot parse multipart request.", e);
}
// ...
}
Alternatively you can also wrap this all in a Filter which parses it all automagically and put the stuff back in the parametermap of the request so that you can continue using request.getParameter() the usual way and retrieve the uploaded file by request.getAttribute(). You can find an example in this blog article.
When you're already on Servlet 3.0 or newer, use native API
If you're already on the fresh new Servlet 3.0 API, then you can just use standard API provided HttpServletRequest#getPart() to collect the individual multipart form data items (most Servlet 3.0 implementations use Apache Commons FileUpload under the covers for this!). This is a bit less convenienced than when using pure Apache Commons FileUpload (a custom utility method is needed to get the filename), but it is workable and easier to use if you already know the input field names beforehand like as you would do with getParameter(). Also, normal form fields are since Servlet 3.0 available by getParameter() the usual way.
First annotate your servlet with @MultipartConfig in order to let it recognize and support multipart/form-data requests and thus get getPart() to work:
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
// ...
}
Then, implement its doPost() as follows:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String description = request.getParameter("description"); // Retrieves <input type="text" name="description">
Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
String filename = getFilename(filePart);
InputStream filecontent = filePart.getInputStream();
// ... (do your job here)
}
private static String getFilename(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String filename = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
return filename.substring(filename.lastIndexOf('/') + 1).substring(filename.lastIndexOf('\\') + 1); // MSIE fix.
}
}
return null;
}
This can all also be done in a Filter which does all the job transparently so that you can continue using the request.getParameter() the usual way. You can find an example in this article.
Workaround for GlassFish bug of getParameter() still returning null
Note that Glassfish versions older than 3.1.2 had a bug wherein the getParameter() still returns null. If you are targeting such a container and can't upgrade it, then you need to extract the value from getPart() as follows instead:
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
With this utility method:
private static String getValue(Part part) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8"));
StringBuilder value = new StringBuilder();
char[] buffer = new char[1024];
for (int length = 0; (length = reader.read(buffer)) > 0;) {
value.append(buffer, 0, length);
}
return value.toString();
}