RESTEasy File Upload Example
In this tutorial we are going to see how In this example we are going to see how you can upload a File to a server using a JAX-RS REST Service using RESTEasy.
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. 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). Unfortunately, RESTEasy does not provide the infrastructure to parse header parts, like you’ve see in Jersey File Upload Example. So, we will have to parse the header ourselves in order to obtain the original name of the file.
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 RESTEasy Hello World Example. But you can also download the Eclipse project of that tutorial here : JAXRS-RESTEasy-CustomApplication.zip, and build your code on top of that.
1. Project structure
For this example, I’ve created a new Project called “JAXRS-RESTEasy
“. You can see the final structure of the project in the image below:
The code presented in this new tutorial will only concern RESTEasyFileUploadService.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>JAXRS-RESTEasy</display-name> <servlet-mapping> <servlet-name>resteasy-servlet</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping> <!-- Auto scan REST service --> <context-param> <param-name>resteasy.scan</param-name> <param-value>true</param-value> </context-param> <!-- this should be the same URL pattern as the servlet-mapping property --> <context-param> <param-name>resteasy.servlet.mapping.prefix</param-name> <param-value>/rest</param-value> </context-param> <listener> <listener-class> org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap </listener-class> </listener> <servlet> <servlet-name>resteasy-servlet</servlet-name> <servlet-class> org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher </servlet-class> </servlet> </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-RESTEasy/rest/
2. RESTEasy Multipart dependencies
In order to use all the classes that RESTEasy offers for multipart media manipulation you have to include resteasy-multipart-provider.jar
to your project dependencies. To resolve this, open your 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.resteasy</groupId> <artifactId>JAXRS-RESTEasy</artifactId> <version>0.0.1-SNAPSHOT</version> <repositories> <repository> <id>JBoss repository</id> <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jaxrs</artifactId> <version>3.0.4.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-multipart-provider</artifactId> <version>3.0.4.Final</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.
RESTEasyFileUploadService.java:
package com.javacodegeeks.enterprise.rest.resteasy; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; @Path("/files") public class RESTEasyFileUploadService { private static final String SERVER_UPLOAD_LOCATION_FOLDER = "C://Users/nikos/Desktop/Upload_Files/"; @POST @Path("/upload") @Consumes("multipart/form-data") public Response uploadFile(MultipartFormDataInput input) { String fileName = ""; Map<String, List<InputPart>> formParts = input.getFormDataMap(); List<InputPart> inPart = formParts.get("file"); for (InputPart inputPart : inPart) { try { // Retrieve headers, read the Content-Disposition header to obtain the original name of the file MultivaluedMap<String, String> headers = inputPart.getHeaders(); fileName = parseFileName(headers); // Handle the body of that part with an InputStream InputStream istream = inputPart.getBody(InputStream.class,null); fileName = SERVER_UPLOAD_LOCATION_FOLDER + fileName; saveFile(istream,fileName); } catch (IOException e) { e.printStackTrace(); } } String output = "File saved to server location : " + fileName; return Response.status(200).entity(output).build(); } // Parse Content-Disposition header to get the original file name private String parseFileName(MultivaluedMap<String, String> headers) { String[] contentDispositionHeader = headers.getFirst("Content-Disposition").split(";"); for (String name : contentDispositionHeader) { if ((name.trim().startsWith("filename"))) { String[] tmp = name.split("="); String fileName = tmp[1].trim().replaceAll("\"",""); return fileName; } } return "randomName"; } // 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
. MultipartFormDataInput
simply represents all the parts of a multiparted form input. We use that to obtain the desired form parts. In our example we want to get the form partfile
, which of course is the uploaded file. Because there might be more than one parts of the form namedfile
, we will get a list of those items, in fact a list ofInputPart
, which simply represents a part of a multiparted input, usinggetFormDataMap()
API method. As we’ve stated in the introduction, every part of a multiparted input starts with certain headers, which can be obtained usinggetHeaders()
API method ofInputPart
interface. We want to search for thefilename
parameter of theContent-Disposition
header and read the original file name. 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
Unfortunately there is no trivial way to do that in REASTEasy, so you have to parse the header yourself, and that’s exactly what we do in
parseFileName
method.
5. Run the code
After deploying your service, open a browser and go to the form URL.
form_URI:
http://localhost:8080/JAXRS-RESTEasy/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 @MultipartForm
RESTEasy offers @MultipartForm
annotation to bind a multipart form with a POJO. Let’s see how you can do that.
First, you have to create a new Java Class. Go to the Package Explorer and Right click on com.javacodegeeks.enterprise.rest.resteasy
package -> New -> Class, and create a new class named MyMultipartForm
.
This would be the final structure of our Project:
Open the newly created file and paste the following code:
MyMultipartForm.java:
package com.javacodegeeks.enterprise.rest.resteasy; import java.io.InputStream; import javax.ws.rs.FormParam; import org.jboss.resteasy.annotations.providers.multipart.PartType; public class MyMultipartForm { @FormParam("file") @PartType("image/png") private InputStream file_input; public InputStream getFile_input() { return file_input; } public void setFile_input(InputStream file_input) { this.file_input = file_input; } }
As you can see the above class has one attribute, an InputStream
that will represent the uploaded file. We use @FormParam
to bind the class attribute with the form input named file
. We also use @PartType("image/png")
to state that the type of the consumed resource will be an image. Here, you can alter the consumed media type, e.g you could put application/pdf
, but you have to bind it to an attribute capable of providing a reader for that type of media (same goes for other media types like application/xml
).
RESTEasyFileUploadService.java:
package com.javacodegeeks.enterprise.rest.resteasy; 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.Response; import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; @Path("/files") public class RESTEasyFileUploadService { private static final String SERVER_UPLOAD_LOCATION_FOLDER = "C://Users/nikos/Desktop/Upload_Files/"; @POST @Path("/upload") @Consumes("multipart/form-data") public Response uploadFile(@MultipartForm MyMultipartForm form) { String fileName = SERVER_UPLOAD_LOCATION_FOLDER + "randomName2.png"; saveFile(form.getFile_input(), fileName); String output = "File saved to server location : " + fileName; 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(); } } }
As you can see the only problem is that we cannot obtain the original file name, which is a bit disturbing. 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.
After deploying your service, open a browser and go to the form URL.
form_URI:
http://localhost:8080/JAXRS-RESTEasy/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 :
When you click submit, as a result you will see the path of the uploaded file on the server:
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-RESTEasy.zip
Nice tutorial, did help me alot
did the same implementation, but always getting an error as “415 Unsupported Media Type”.
Never Worked for me