This tutorial is a quick guide for handling files upload and download using REST Services. We will demonstrate how to upload and download files using JAX-RS API and how to build a JUnit Test to test files uploading using RESTEasy Client API.

Uploading and Downloading files using a REST Service

In order to manage files Upload and Download we will be using the core JAX-RS API and Rest Easy implementation, along with IOUtil class from Apache Commons IO library. Here is the source code of our REST Service:

package com.mastertheboss.rest;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.apache.commons.io.IOUtils;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;

@Path("/file")
public class RestFilesDemo {
	@POST
	@Path("/upload")
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	public Response uploadFile(MultipartFormDataInput input) throws IOException {

		Map<String, List<InputPart>> uploadForm = input.getFormDataMap();

		// Get file data to save
		List<InputPart> inputParts = uploadForm.get("attachment");

		for (InputPart inputPart : inputParts) {
			try {

				MultivaluedMap<String, String> header = inputPart.getHeaders();
				String fileName = getFileName(header);

				// convert the uploaded file to inputstream
				InputStream inputStream = inputPart.getBody(InputStream.class, null);

				byte[] bytes = IOUtils.toByteArray(inputStream);

				String path = System.getProperty("user.home") + File.separator + "uploads";
				File customDir = new File(path);

				if (!customDir.exists()) {
					customDir.mkdir();
				}
				fileName = customDir.getCanonicalPath() + File.separator + fileName;
				writeFile(bytes, fileName);

				return Response.status(200).entity("Uploaded file name : " + fileName).build();

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	@POST
	@Path("/download")
	@Produces(MediaType.APPLICATION_OCTET_STREAM)
	@Consumes("application/x-www-form-urlencoded")
	public Response downloadFileWithPost(@FormParam("file") String file) {
		String path = System.getProperty("user.home") + File.separator + "uploads";
		File fileDownload = new File(path + File.separator + file);
		ResponseBuilder response = Response.ok((Object) fileDownload);
		response.header("Content-Disposition", "attachment;filename=" + file);
		return response.build();
	}

	@GET
	@Path("/download")
	@Produces(MediaType.APPLICATION_OCTET_STREAM)
	public Response downloadFileWithGet(@QueryParam("file") String file) {

		String path = System.getProperty("user.home") + File.separator + "uploads";
		File fileDownload = new File(path + File.separator + file);
		ResponseBuilder response = Response.ok((Object) file);
		response.header("Content-Disposition", "attachment;filename=" + file);
		return response.build();
	}

	private String getFileName(MultivaluedMap<String, String> header) {

		String[] contentDisposition = header.getFirst("Content-Disposition").split(";");

		for (String filename : contentDisposition) {

			if ((filename.trim().startsWith("filename"))) {

				String[] name = filename.split("=");

				String finalFileName = name[1].trim().replaceAll("\"", "");
				return finalFileName;
			}
		}
		return "unknown";
	}

	// Utility method
	private void writeFile(byte[] content, String filename) throws IOException {
		File file = new File(filename);

		if (!file.exists()) {
			file.createNewFile();
		}
		FileOutputStream fop = new FileOutputStream(file);
		fop.write(content);
		fop.flush();
		fop.close();
		System.out.println("Written: " + filename);
	}
}

 Following here are some details about the methods used:

  • uploadFile(MultipartFormDataInput input) : this method is used to upload a File, consuming a MULTIPART_FORM_DATA from an HTTP POST method
  • downloadFileWithPost(@FormParam("file") String file) : this method is used to download a File, producing a Binary Stream, from an HTTP POST passing the filename as a Form argument
  • downloadFileWithGet(@QueryParam("file") String file): this method is used as well to download a File but it can be used with an HTTP GET passing the filename as Query argument

Coding the Front end

Even if you could use any REST Client (even cURL) to reach the REST Service, we will provide a simple front-end application that can be used to interact with the REST Service and provide a list of available files in the folder used to upload/download.

Here is the index.jsp page:

<%@page import="java.io.*" %> 
<%@page import="java.util.*" %> 
<%!        public void getDirectory(String a_Path, Vector a_files, Vector a_folders) {
        File l_Directory = new File(a_Path);
        File[] l_files = l_Directory.listFiles();

        for (int c = 0; c < l_files.length; c++) {
            if (l_files[c].isDirectory()) {
                a_folders.add(l_files[c].getName());
            } else {
                a_files.add(l_files[c].getName());
            }
        }


    }
%> 


<html>
<head>
<title>Start Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
	<h2>REST Upload demo</h2>
	<form method="post" action="rest/demo/upload"
		enctype="multipart/form-data">
		<input type="hidden" name="action" value="upload" /> <label>Load
			your file:</label> <input type="file" name="attachment" /> <br /> <input
			type="submit" value="Upload file" />
	</form>

	<h2>REST Download demo</h2>
	<form method="POST" action="rest/demo/download">
		File name: <input type="text" name="file"> 
		<input type="submit">


	</form>
	
	<h2>Current Directory $HOME/uploads:</h2>
	<br/>
	        <%
            Vector l_Files = new Vector(), l_Folders = new Vector();
            getDirectory(System.getProperty("user.home") + File.separator + "uploads", l_Files, l_Folders);
            out.println("<ul>");   
            for (int a = 0; a < l_Files.size(); a++) {
                out.println("<li>" + l_Files.elementAt(a).toString() + "</li>");
            }
            out.println("</ul>");
           
        %> 
</body>
</html>

Please note that the file component name ("attachment") needs to match with the information extracted from the REST Service.

Adding a JUnit Test class to test REST upload

We will finally include a JUnit Test class which shows the hardest part of it: managing MultiPart requests to upload a File with REST Easy Client API:

package com.mastertheboss.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.FileInputStream;

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
import org.junit.Test;

public class TestRest {

    @Test
    public void sendFile() throws Exception {
         
    	ResteasyClient client = new ResteasyClientBuilder().build();
    	ResteasyWebTarget target = client.target("http://localhost:8080/rest-file-manager/rest/file/upload");
    	
    	MultipartFormDataOutput mdo = new MultipartFormDataOutput();
    	
    	// Specify file name to be uploaded in pom.xml
    	String fileNamePath = System.getProperty("fileName");
    	File filePath = new File(fileNamePath);
    	
    	// Check that file exists
    	assertTrue(filePath.exists());
    	
    	mdo.addFormData("attachment", new FileInputStream(new File(fileNamePath)),
    		    MediaType.APPLICATION_OCTET_STREAM_TYPE,filePath.getName());
    	
    	GenericEntity<MultipartFormDataOutput> entity = new GenericEntity<MultipartFormDataOutput>(mdo) {};
    	
    	Response r = target.request().post( Entity.entity(entity, MediaType.MULTIPART_FORM_DATA_TYPE));
    	
    	// Check that upload was successful
    	assertEquals(r.getStatus(), 200);
    	
    	 
   }

}

In the above test, we have provided the filename to be uploaded as a System Property "filename". The pom.xml injects the System Property as you can see in the full pom.xml which I have used to build and deploy the application:

<?xml version="1.0" encoding="UTF-8"?>
<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.mastertheboss</groupId>
	<artifactId>rest-file-manager</artifactId>
	<version>1.1</version>
	<packaging>war</packaging>

	<name>rest-file-manager</name>

	<properties>
		<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<version.server.bom>15.0.1.Final</version.server.bom>

	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.wildfly.bom</groupId>
				<artifactId>wildfly-javaee8-with-tools</artifactId>
				<version>${version.server.bom}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.6</version>
		</dependency>

		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-jaxrs</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-multipart-provider</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.jboss.spec.javax.ws.rs</groupId>
			<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
			<scope>provided</scope>
		</dependency>


		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-client</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<finalName>rest-file-manager</finalName>
		<plugins>
			<plugin>
				<groupId>org.wildfly.plugins</groupId>
				<artifactId>wildfly-maven-plugin</artifactId>
				<version>2.0.0.Final</version>
				<configuration>
					<filename>${project.build.finalName}.war</filename>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<compilerArguments>
						<endorseddirs>${endorsed.dir}</endorseddirs>
					</compilerArguments>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<version>2.6</version>
				<executions>
					<execution>
						<phase>validate</phase>
						<goals>
							<goal>copy</goal>
						</goals>
						<configuration>
							<outputDirectory>${endorsed.dir}</outputDirectory>
							<silent>true</silent>
							<artifactItems>
								<artifactItem>
									<groupId>javax</groupId>
									<artifactId>javaee-endorsed-api</artifactId>
									<version>7.0</version>
									<type>jar</type>
								</artifactItem>
							</artifactItems>
						</configuration>
					</execution>
				</executions>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<configuration>
					<systemPropertyVariables>
						<fileName>/home/jboss/file.txt</fileName>
					</systemPropertyVariables>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

You can build and deploy with: 

$ mvn clean install wildfly:deploy -DskipTests=true

Here is the home page of this application in action:

rest file upload download demo resteasy

On the other hand, if you want to test the JUnit upload, just run:

$ mvn clean test

Tesing with cURL

You can test Files uploading as well with cURL:

$ curl -F file=@"/tmp/file.txt" http://localhost:8080/rest-file-manager/rest/file/upload
Successfully uploaded - file.txt

Source code of this tutorial

You can find the source code for this tutorial, updated for WildFly 15, on github at: https://github.com/fmarchioni/mastertheboss/tree/master/javaee/rest-file-manager

0
0
0
s2smodern