Categories
file-upload jakarta-ee java jsp servlets

How can I upload files to a server using JSP/Servlet?

716

How can I upload files to server using JSP/Servlet?

I tried this:

<form action="upload" method="post">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

However, I only get the file name, not the file content. When I add enctype="multipart/form-data" to the <form>, then request.getParameter() returns null.

During research I stumbled upon Apache Common FileUpload. I tried this:

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); // This line is where it died.

Unfortunately, the servlet threw an exception without a clear message and cause. Here is the stacktrace:

SEVERE: Servlet.service() for servlet UploadServlet threw exception
javax.servlet.ServletException: Servlet execution threw an exception
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:637)

4

  • Perhaps this article will be helpful: baeldung.com/upload-file-servlet

    Jan 15, 2019 at 4:31

  • 1

    @Adam: They copied from my answer and added a sleuth of advertising on top of it in an attempt to earn money with it. Yeah, great article ..

    – BalusC

    Feb 24, 2021 at 11:24

  • 1

    No, actually nothing was copied. I wrote the first draft of that article along with the supplemental code. The core reference documentation can be found here: commons.apache.org/proper/commons-fileupload/using.html (and is linked to and cited in the article). Examples are partly reprised from the core reference document (which is the point of reference documentation – i.e. to be a point of reference) but not in their entirety (note that the reference docs don’t go into much detail). Thanks!

    Mar 29, 2021 at 15:33


  • check this sandny.com/2017/05/18/servlet-file-upload

    – ricky

    Jul 26, 2021 at 16:50


1248

+200

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 (Dec 2009), 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. This is where the well known Apache Commons FileUpload came into the picture.

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. See also uploading of pdf file. 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 already on Servlet 3.0 or newer, use native API

If you’re using at least Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, etc, they exist already since 2010), then you can just use standard API provided HttpServletRequest#getPart() to collect the individual multipart form data items (most Servlet 3.0 implementations actually use Apache Commons FileUpload under the covers for this!). Also, normal form fields are 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 = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
    InputStream fileContent = filePart.getInputStream();
    // ... (do your job here)
}

Note the Path#getFileName(). This is a MSIE fix as to obtaining the file name. This browser incorrectly sends the full file path along the name instead of only the file name.

In case you want to upload multiple files via either multiple="true",

<input type="file" name="files" multiple="true" />

or the old-fashioned way with multiple inputs,

<input type="file" name="files" />
<input type="file" name="files" />
<input type="file" name="files" />
...

then you can collect them as below (unfortunately there is no such method as request.getParts("files")):

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // ...
    List<Part> fileParts = request.getParts().stream().filter(part -> "files".equals(part.getName()) && part.getSize() > 0).collect(Collectors.toList()); // Retrieves <input type="file" name="files" multiple="true">

    for (Part filePart : fileParts) {
        String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
        InputStream fileContent = filePart.getInputStream();
        // ... (do your job here)
    }
}

When you’re not on Servlet 3.1 yet, manually get submitted file name

Note that Part#getSubmittedFileName() was introduced in Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4, etc, they exist since 2013 already). If you’re not on Servlet 3.1 yet (really?), then you need an additional utility method to obtain the submitted file name.

private static String getSubmittedFileName(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("https://stackoverflow.com/") + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
        }
    }
    return null;
}
String fileName = getSubmittedFileName(filePart);

Note the MSIE fix as to obtaining the file name. This browser incorrectly sends the full file path along the name instead of only the file name.

When you’re not on Servlet 3.0 yet, use Apache Commons FileUpload

If you’re not on Servlet 3.0 yet (isn’t it about time to upgrade? it’s released over a decade ago!), 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);
    }

    // ...
}

It’s very important that you don’t call getParameter(), getParameterMap(), getParameterValues(), getInputStream(), getReader(), etc on the same request beforehand. Otherwise the servlet container will read and parse the request body and thus Apache Commons FileUpload will get an empty request body. See also a.o. ServletFileUpload#parseRequest(request) returns an empty list.

Note the FilenameUtils#getName(). This is a MSIE fix as to obtaining the file name. This browser incorrectly sends the full file path along the name instead of only the file name.

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.

Workaround for GlassFish3 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() with help of 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();
}
String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">
    

Saving uploaded file (don’t use getRealPath() nor part.write()!)

Head to the following answers for detail on properly saving the obtained InputStream (the fileContent variable as shown in the above code snippets) to disk or database:

Serving uploaded file

Head to the following answers for detail on properly serving the saved file from disk or database back to the client:

Ajaxifying the form

Head to the following answers how to upload using Ajax (and jQuery). Do note that the servlet code to collect the form data does not need to be changed for this! Only the way how you respond may be changed, but this is rather trivial (i.e. instead of forwarding to JSP, just print some JSON or XML or even plain text depending on whatever the script responsible for the Ajax call is expecting).


Hope this all helps 🙂

17

  • Ah sorry, I was seeing request.getParts("file") and was confused x_x

    Nov 5, 2016 at 22:02

  • With Servlet 3.0, if a MultipartConfig condition is violated (eg: maxFileSize), calling request.getParameter() returns null. Is this on purpose? What if I get some regular (text) parameters before calling getPart (and checking for an IllegalStateException)? This causes a NullPointerException to be thrown before I have a chance to check for the IllegalStateException.

    – theyuv

    Nov 20, 2016 at 14:34


  • @BalusC I created a post related to this, do you have an idea how I could retrieve extra infos from File API webKitDirectory. More details here stackoverflow.com/questions/45419598/…

    – Rapster

    Jul 31, 2017 at 23:52

  • 1

    Yeah, if someone tries to use the code in 3.0 section with tomcat 7, they might face issue in String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.part similar to me

    – raviraja

    Sep 24, 2018 at 13:24


  • 2

    @aaa: that can happen when you converted bytes to characters by using a Reader and/or Writer for unclear reason. Do not do that. Use InputStream/OutputStream over all place during reading and writing an uploaded file without massaging bytes into characters. A PDF file is not a character based text file or so. It is a binary file.

    – BalusC

    Apr 1, 2021 at 10:01


26

If you happen to use Spring MVC, this is how to (I’m leaving this here in case someone find it useful):

Use a form with enctype attribute set to “multipart/form-data” (the same as BalusC’s answer):

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" value="Upload"/>
</form>

In your controller, map the request parameter file to MultipartFile type as follows:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public void handleUpload(@RequestParam("file") MultipartFile file) throws IOException {
    if (!file.isEmpty()) {
            byte[] bytes = file.getBytes(); // alternatively, file.getInputStream();
            // application logic
    }
}

You can get the filename and size using MultipartFile‘s getOriginalFilename() and getSize().

I’ve tested this with Spring version 4.1.1.RELEASE.

1

  • If I’m not mistaken, this requires that you configure a bean in your server’s application config…

    Jul 19, 2018 at 21:36

13

Without components or external libraries in Tomcat 6 or Tomcat 7

Enabling upload in the web.xml file:

Manually Installing PHP, Tomcat and Httpd Lounge.

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <multipart-config>
      <max-file-size>3145728</max-file-size>
      <max-request-size>5242880</max-request-size>
    </multipart-config>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

As you can see:

<multipart-config>
  <max-file-size>3145728</max-file-size>
  <max-request-size>5242880</max-request-size>
</multipart-config>

Uploading files using JSP. files:

In the HTML file

<form method="post" enctype="multipart/form-data" name="Form" >

  <input type="file" name="fFoto" id="fFoto" value="" /></td>
  <input type="file" name="fResumen" id="fResumen" value=""/>

In the JSP File or Servlet

InputStream isFoto = request.getPart("fFoto").getInputStream();
InputStream isResu = request.getPart("fResumen").getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buf[] = new byte[8192];
int qt = 0;
while ((qt = isResu.read(buf)) != -1) {
  baos.write(buf, 0, qt);
}
String sResumen = baos.toString();

Edit your code to servlet requirements, like max-file-size, max-request-size and other options that you can to set…

0