Jersey File Upload Example
In this example we are going to see how you can upload a File to a server using a JAX-RS REST Service using Jersey. Uploading a File using Jersey is fairly easy, as it uses all the HTTP infrastructure for file upload operations.
In this example we are going to use an HTML Form that has one input field of type file
. When the HTTP POST request is constructed, it will contain a media type of multipart/form-data
. The media-type multipart/form-data
follows the rules of all multipart MIME data streams. multipart/form-data
contains a number of parts, corresponding to the input parameters of the form. Each part contains a content-disposition
header where the disposition type is form-data
. The disposition, also contains a “name
” parameter, the value of which is the input field name in the HTML form and can be used to obtain this header in our service. Other headers like content-type
are usually included as well. For example, a part might contain a header like this:
Content-Disposition: form-data; name="file"; filename="AVD1.png" Content-Type: image/png
In our case, parsing this header we will enable us to obtain the original name of the file the user selected to upload (the filename
parameter of the above header). Luckily, Jersey provides all the necessary infrastructure to do that. Following the headers is the the actual value of the part, as expected.
In this example we are not going to focus on how to create JAX-RS application from top to bottom. So make sure you read carefully Jersey Hello World Example and pay attention to the sections concerning the creation of the project with Eclipse IDE as well as the deployment of the project in Tomcat.
You can create your own project following the instructions on Jersey Hello World Example. But you can also download the Eclipse project of this tutorial here : JAXRS-HelloWorld.zip, and build your code on top of that.
1. Project structure
For this example, I’ve created a new Project called “JAXRS-FileUpload
“. You can see the final structure of the project in the image below:
The code presented in this new tutorial will only concern JerseyFileUpload.java
file.
At this point you can also take a look at the web.xml
file to see how the project is configured:
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Restful Web Application</display-name> <servlet> <servlet-name>jersey-helloworld-serlvet</servlet-name> <servlet-class> com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.javacodegeeks.enterprise.rest.jersey</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jersey-helloworld-serlvet</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> </web-app>
As you can see our servlet is mapped to /rest/
URI pattern. So the basic structure of the URIs to reach the REST Services used in this example will have the form :
http://localhost:8080/JAXRS-FileUpload/....
2. Jersey Multipart dependencies
In order to use all the classes that Jersey offers for multipart media manipulation you have to include jersey-multipart.jar
to your project dependencies. To resolve this, open pom.xml
and paste the following code:
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.enterprise.rest.jersey</groupId> <artifactId>JAXRS-HelloWorld</artifactId> <version>0.0.1-SNAPSHOT</version> <repositories> <repository> <id>maven2-repository.java.net</id> <name>Java.net Repository for Maven</name> <url>http://download.java.net/maven/2/</url> <layout>default</layout> </repository> </repositories> <dependencies> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-server</artifactId> <version>1.9</version> </dependency> <dependency> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-multipart</artifactId> <version>1.9</version> </dependency> </dependencies> </project>
3. HTML Upload Form
This is of course to host a simple HTML form to demonstrate the use of file uploading. Go to the Package Explorer, Right Click on the project -> New -> HTML File. The new file will be created in the WebContent
folder.
form.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Form Page</title> </head> <body> <h1>Upload a File</h1> <form action="rest/files/upload" method="post" enctype="multipart/form-data"> <p> Select a file : <input type="file" name="file" size="50" /> </p> <input type="submit" value="Upload It" /> </form> </body> </html>
4. Upload REST Service
Let’s see the code of the JAX-RS REST Service and then discuss the important points.
JerseyFileUpload.java:
package com.javacodegeeks.enterprise.rest.jersey; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.sun.jersey.core.header.FormDataContentDisposition; import com.sun.jersey.multipart.FormDataParam; @Path("/files") public class JerseyFileUpload { private static final String SERVER_UPLOAD_LOCATION_FOLDER = "C://Users/nikos/Desktop/Upload_Files/"; /** * Upload a File */ @POST @Path("/upload") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFile( @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition contentDispositionHeader) { String filePath = SERVER_UPLOAD_LOCATION_FOLDER + contentDispositionHeader.getFileName(); // save the file to the server saveFile(fileInputStream, filePath); String output = "File saved to server location : " + filePath; return Response.status(200).entity(output).build(); } // save uploaded file to a defined location on the server private void saveFile(InputStream uploadedInputStream, String serverLocation) { try { OutputStream outpuStream = new FileOutputStream(new File(serverLocation)); int read = 0; byte[] bytes = new byte[1024]; outpuStream = new FileOutputStream(new File(serverLocation)); while ((read = uploadedInputStream.read(bytes)) != -1) { outpuStream.write(bytes, 0, read); } outpuStream.flush(); outpuStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
Let’s discuss the above code in detail:
- The
@Consumes
annotation is used to specify whichMIME
media types a service can consume from the client. In our case it’sMediaType.MULTIPART_FORM_DATA
. @FormDataParam
binds the named body part of amultipart/form-data
request entity to a method parameter. The type of the annotated parameter can be a class that is able to read that particular media type. In this example, the server consumes amultipart/form-data
request entity body that contains one body part, namedfile
, which is of course the uploaded file. The value of the partfile
will be handled by anInputStream
.- Additional information about the file from the “
Content-Disposition
” header are injected tocontentDispositionHeader
parameter of typeFormDataContentDisposition
, which is simply a representation of theContent-Disposition
Header. In this case we can obtain the original name of the uploaded file. To give you an example of howcontent-disposition
works in a multipart form, here is aPOST
request when uploading and image:POST Request:POST /JAXRS-FileUpload/rest/files/upload HTTP/1.1 Host: localhost:8080 Connection: keep-alive Content-Length: 25606 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Origin: http://localhost:9090 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4eqSAKp0q8C1bMNj DNT: 1 Referer: http://localhost:9090/JAXRS-FileUpload/form.html Accept-Encoding: gzip,deflate,sdch Accept-Language: el,en;q=0.8,en-GB;q=0.6 ------WebKitFormBoundary4eqSAKp0q8C1bMNj Content-Disposition: form-data; name="file"; filename="AVD1.png" Content-Type: image/png ‰PNG
5. Run the code
After deploying your service, open a browser and go to the form URL.
form_URI:
http://localhost:8080/JAXRS-FileUpload/form.html
Here it is on the browser:
If you press “Choose File” a file selection dialogue will pop up. I’ve randomly selected an image from my Desktop.
Click “Open” and you are ready to submit the form. You can see the original name of the file:
When you click submit, as a result you will see the path of the uploaded file on the server:
6. Using FormDataMultiPart
You can also use the FormDataMultiPart class, that simply represents the HTML form and its parts. As you will see it is very convenient when used in a form with a big number of multipart fields. Packing them all in one Object means that you don’t have to define a lot of arguments in your method, plus you will be able to handle fields with arbitrary names etc. Let’s see how you can use it :
JerseyFileUpload.java:
package com.javacodegeeks.enterprise.rest.jersey; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.sun.jersey.core.header.ContentDisposition; import com.sun.jersey.multipart.FormDataBodyPart; import com.sun.jersey.multipart.FormDataMultiPart; @Path("/files") public class JerseyFileUpload { private static final String SERVER_UPLOAD_LOCATION_FOLDER = "C://Users/nikos/Desktop/Upload_Files/"; /** * Upload a File */ @POST @Path("/upload") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response uploadFile(FormDataMultiPart form) { FormDataBodyPart filePart = form.getField("file"); ContentDisposition headerOfFilePart = filePart.getContentDisposition(); InputStream fileInputStream = filePart.getValueAs(InputStream.class); String filePath = SERVER_UPLOAD_LOCATION_FOLDER + headerOfFilePart.getFileName(); // save the file to the server saveFile(fileInputStream, filePath); String output = "File saved to server location using FormDataMultiPart : " + filePath; return Response.status(200).entity(output).build(); } // save uploaded file to a defined location on the server private void saveFile(InputStream uploadedInputStream, String serverLocation) { try { OutputStream outpuStream = new FileOutputStream(new File( serverLocation)); int read = 0; byte[] bytes = new byte[1024]; outpuStream = new FileOutputStream(new File(serverLocation)); while ((read = uploadedInputStream.read(bytes)) != -1) { outpuStream.write(bytes, 0, read); } outpuStream.flush(); outpuStream.close(); uploadedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
As you can see we define a FormDataMultiPart
argument for uploadFile
method. Then, we use getField
API method of FormDataMultiPart
class to obtain a FormDataBodyPart
instance that simply represents a body part of the form. In our case we choose the part named file
. You can then call getContentDisposition
API method of FormDataBodyPart
class to get a ContentDisposition
instance (that obviously represents a content-disposition
header). Next you can call getValueAs
API method of FormDataBodyPart
class to read the value of that particular form field. You can also choose a suitable reader for that type of media. We choose InputStream
.
Let’s run it. Here is the upload form:
And when you hit “Upload It”:
Notes
It is important to note that you should be careful when using Content-Disposition headers as they suffer from several security pitfalls, many of which can be found on it’s original documentation. Furthermore some browsers do not implement correctly the demonstrated functionality, because they pass the full path of the uploaded file as the fileName
. This is the case with Internet Explorer. Instead of AVD1.png
, you could get the full file path of the image : C:\Users\nikos\Desktop\AVD1.png
. But, because ‘\’ should be escaped in HTTP/1.1 requests, the file name you’ll get in your service will be C:UsersnikosDesktopAVD1.png
, which is a total mess. That is not an easy problem to overcome. One solution you can use is read the file path using Javascript and then either parse the file path to obtain the name, or send the name with the slashes as a hidden parameter. Having said that, it might not be very important for your application to store the file to the server using the original file name, not to mention that sometimes this is absolutely wrong and dangerous.
Donwload The Eclipse Project
This was an example on how to upload files to a server using JAX-RS with Jersey. Download the Eclipse project of this example : JAXRS-FileUpload.zip, JAXRS-UsingFormData.zip