resteasy

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:

project-structure

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 which MIME 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 part file, which of course is the uploaded file. Because there might be more than one parts of the form named file, we will get a list of those items, in fact a list of InputPart, which simply represents a part of a multiparted input, using getFormDataMap() API method. As we’ve stated in the introduction, every part of a multiparted input starts with certain headers, which can be obtained using getHeaders() API method of InputPart interface. We want to search for the filename parameter of the Content-Disposition header and read the original file name. To give you an example of how content-disposition works in a multipart form, here is a POST 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:

form

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:

form-ready

When you click submit, as a result you will see the path of the uploaded file on the server:

result

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.

new-class

This would be the final structure of our Project:

final-structure

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:

form

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 :

form-ready

When you click submit, as a result you will see the path of the uploaded file on the server:

result-random-name

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

Nikos Maravitsas

Nikos has graduated from the Department of Informatics and Telecommunications of The National and Kapodistrian University of Athens. During his studies he discovered his interests about software development and he has successfully completed numerous assignments in a variety of fields. Currently, his main interests are system’s security, parallel systems, artificial intelligence, operating systems, system programming, telecommunications, web applications, human – machine interaction and mobile development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Kirathe
Kirathe
5 years ago

Nice tutorial, did help me alot

ashish kumar
ashish kumar
5 years ago

did the same implementation, but always getting an error as “415 Unsupported Media Type”.
Never Worked for me

Back to top button