Upload Files in Java with Servlet API 3.1

This example demonstrates how to handle file uploads in Java web application with Servlet API 3.1.

Requirements:

  • Java 1.7+;
  • Servlet container with Servlet API 3.1 support, e.g. Jetty 9.2+, Tomcat 8.0+, etc.

Download and play

You can download sources and play with this example in your preferred IDE.

  • Download and unpack the latest project bundle.
  • Open Maven project in the folder examples/servlet-api-example
  • Start com.ursaj.hfs.example.servlet.Main class from the tests scope.
  • Try the example http://localhost:8080 in the browser.

Basic example how to upload files in Java with Servlet API 3.1

Application structure

This example consist of the following files:

/WEB-INF/web.xml
Web application deployment descriptor.
/WEB-INF/classes/.../UploadServlet.class
Servlet to handle file uploads and UI requests.
/WEB-INF/classes/.../FileInfo.class
Simple POJO to keep uploaded file information.
/WEB-INF/pages/upload.jsp
Upload form template.
/WEB-INF/tags/page.tag
Application UI template.

Deployment descriptor

/WEB-INF/web.xml deployment descriptor is optional as this example is configured via annotations. You can omit it or provide an empty stub.

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
</web-app>

Keep in mind the main concern against this approach – to modify any single parameter you have to:

  • change the code and re-build your application or
  • provide complete servlet configuration in the deployment descriptor (servlet definition, initialization parameters, multipart-configuration, URI mapping, etc).

Servlet declaration

@WebServlet annotation provides a binding between your servlet code and web application URI.

@WebServlet(urlPatterns = { "/upload" }, ... )
public class UploadServlet extends HttpServlet {
    protected void doGet ...
    protected void doPost ...

Multipart requests support

@MultipartConfig annotation enables your servlet to handle 'multipart/form-data' POST requests.

@WebServlet ...
@MultipartConfig(
    fileSizeThreshold = 16768,
    maxRequestSize = 10L * 1024 * 1024,
    maxFileSize = 10L * 1024 * 1024
)
public class UploadServlet extends HttpServlet {
    protected void doGet ...
    protected void doPost ...

Handle upload request

Parts API is the essential part of Servlet API 3.1 to handle file uploads. It implements RFC-2047 and allows to work with 'multipart/form-data' request entities.

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Deque<FileInfo> files = new LinkedList<>();
    for (Part part : req.getParts()) {
        long fileSize = part.getSize();
        String fileName = part.getSubmittedFileName();
        if (fileSize == 0 && (fileName == null || fileName.isEmpty())) {
            continue; // Ignore part, if not a file.
        }
        FileInfo info = new FileInfo(UUID.randomUUID(), fileSize, fileName, part.getContentType());

        files.add(info);
        Files.copy(part.getInputStream(), new File(uploads, info.getId().toString()).toPath());
    }

    req.getSession().setAttribute("uploadedFiles", files);
    resp.sendRedirect(applicationUrl + "/upload");
}

Note! It is the best practice rule to finish POST request handling with the redirect response.

Handle UI request

GET requests are handled with the same servlet class. However your are free to split user tracks between different handlers and redirect your users wherever you want.

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    @SuppressWarnings("unchecked") List<FileInfo> uploadedFiles =
        (List<FileInfo>) req.getSession().getAttribute("uploadedFiles");

    if (uploadedFiles == null)
        uploadedFiles = Collections.emptyList();

    req.setAttribute("applicationUrl", applicationUrl);
    req.setAttribute("uploadedFiles", uploadedFiles);
    req.setAttribute("uploadUri", req.getContextPath() + "/upload");
    getServletContext().getRequestDispatcher("/WEB-INF/pages/upload.jsp").forward(req, resp);
}

Render upload form

Template /WEB-INF/pages/upload.jsp renders both upload files form and uploaded files report.

<%@page contentType="text/html" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>
<jsp:useBean id="uploadedFiles" scope="request" type="java.util.Collection<com.ursaj.hfs.example.servlet.FileInfo>"/>
<jsp:useBean id="uploadUri" scope="request" type="java.lang.String"/>

<t:page title="Basic example how to upload files with Servlet API 3.1">
    <form action="${uploadUri}"
          enctype="multipart/form-data" method="post">
        <input type="file" name="file" multiple="multiple"/>
        <input type="submit" value="Upload"/>
    </form>

    <h3>Uploaded files information</h3>
    <table>
        <tr>
            <th>#</th>
            <th>ID</th>
            <th>Size</th>
            <th>Name</th>
            <th>Content type</th>
        </tr>
        <c:forEach var="fileInfo" varStatus="status" items="${uploadedFiles}">
            <tr>
                <td>${status.count}</td>
                <td>${fileInfo.id}</td>
                <td>${fileInfo.size}</td>
                <td>${fileInfo.fileName}</td>
                <td>${fileInfo.contentType}</td>
            </tr>
        </c:forEach>
        <c:choose>
            <c:when test="${empty uploadedFiles}">
                <tr>
                    <td colspan="5">
                        <p>No information about uploaded files.</p>
                    </td>
                </tr>
            </c:when>
        </c:choose>
    </table>
</t:page>

Uniform application UI

Page template /WEB-INF/tags/page.tag keeps your application uniform across multiple pages.

<%@tag description="Page Template" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@attribute name="title" type="java.lang.String" required="true" rtexprvalue="true" %>
<%@attribute name="head" fragment="true" required="false" %>
<jsp:useBean id="applicationUrl" scope="request" type="java.lang.String"/>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
    <title>${title}</title>
    <link href="${applicationUrl}/css/style.css" rel="stylesheet" type="text/css"/>
    <jsp:invoke fragment="head"/>
</head>
<body>

<h3>${title}</h3>

<jsp:doBody/>

</body>
</html>

Pros and cons

While it seems a trivial task to handle file uploads with Servlet API 3.1, you should consider all aspects of its usage in the real applications.

Pros

  • Simple, Servlet API is ease to use and maintain.
  • Standard, it works in most of modern application containers.
  • Sufficient, it solves the main task – allows to handle file uploads.

Cons

  • Inefficient, upload traffic goes through application server.
  • Incomplete, Servlet API does not cover uploaded files management and publishing.

Read more