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)
Best Answer
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 thePOST
method and theenctype
attribute of the form has to be set to"multipart/form-data"
.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 ofapplication/x-www-form-urlencoded
. Therequest.getParameter()
and consorts would all returnnull
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), 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 bygetParameter()
the usual way.First annotate your servlet with
@MultipartConfig
in order to let it recognize and supportmultipart/form-data
requests and thus getgetPart()
to work:Then, implement its
doPost()
as follows: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"
,or the old-fashioned way with multiple inputs,
then you can collect them as below (unfortunately there is no such method as
request.getParts("files")
):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). If you're not on Servlet 3.1 yet, then you need an additional utility method to obtain the submitted file name.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?), 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
:commons-fileupload.jar
commons-io.jar
Your initial attempt failed most likely because you forgot the commons IO.
Here's a kickoff example how the
doPost()
of yourUploadServlet
may look like when using Apache Commons FileUpload: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 usingrequest.getParameter()
the usual way and retrieve the uploaded file byrequest.getAttribute()
. You can find an example in this blog article.Workaround for GlassFish3 bug of
getParameter()
still returningnull
Note that Glassfish versions older than 3.1.2 had a bug wherein the
getParameter()
still returnsnull
. If you are targeting such a container and can't upgrade it, then you need to extract the value fromgetPart()
with help of this utility method:Saving uploaded file (don't use
getRealPath()
norpart.write()
!)Head to the following answers for detail on properly saving the obtained
InputStream
(thefileContent
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 :)