Using REST Services to upload and download files

This REST Service tutorial is a quick guide for handling files upload and download using REST Services. We will create and test a Rest Service to upload and download files using JAX-RS API. Finally, we will show how to build a JUnit 5 Test to test files uploading using RESTEasy Client API.

Uploading and Downloading files using a REST Service

  • Requirements: Know about JAX-RS. To get started we recommend this tutorial: RESTEasy tutorial

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 endpoint:

@Path("/file")
public class RestFilesDemo {

	@Context
	private ServletContext context;

	@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(Config.UPLOAD_FOLDER);

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

				return Response.status(200).entity("Uploaded file name : " + fileName+" . <br/> <a href='"+context.getContextPath()+"'>Back</a>").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) {
		System.out.println("Download file "+file);
		File fileDownload = new File(Config.UPLOAD_FOLDER + File.separator + file);
		ResponseBuilder response = Response.ok((Object) fileDownload);
		response.header("Content-Disposition", "attachment;filename=" + file);
		return response.build();
	}

	@GET
	@Path("/list")
	@Produces(MediaType.APPLICATION_JSON)
	public List<String> listFiles() {

		List<String> listFiles = new ArrayList<>();
		File fileFolder = new File(Config.UPLOAD_FOLDER);
		File[] list = fileFolder.listFiles();

		for (File f: list) {
			if (!f.isDirectory()) {
				listFiles.add(f.getName());
			}
		}
		return listFiles;
	}

	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();
	}
}

Following here are some details about the methods used:

  • listFiles: this method returns the list of Files which are available in the Config.UPLOAD_FOLDER
  • uploadFile(MultipartFormDataInput input) : this method uploads a File, consuming a MULTIPART_FORM_DATA from an HTTP POST method
  • downloadFileWithPost(@FormParam(“file”) String file) : this method downloads a File, producing a Binary Stream, from an HTTP POST passing the filename as a Form argument
  • downloadFileWithGet(@QueryParam(“file”) String file): this method also downloads a File but it includes the filename as Query argument

Next, to ensure that the “uploads” folder exists, the following Context Listener will create the folder after deploying the application:

@WebListener
public class WebContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        if (!new File(Config.UPLOAD_FOLDER).exists()) {
            new File(Config.UPLOAD_FOLDER).mkdir();
        }

    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //. . .
    }
}

Coding the Front end for the Rest Service sample

There are several ways to rest our REST uploader service. Here we will show a collection of REST Client which can send a File as MultiPart Form Data.

1) Using Postman to upload Files

If you are using Postman to test your REST Services, all you need to do is create a New | HTTP request.

From the HTTP Request UI:

  1. Enter the URL of the REST Service (f.e. http://localhost:8080/rest-file-manager/rest/file/upload )
  2. Select POST as method
  3. Select form-data in the Body
  4. Enter as key “attachment” and choose as type File.
  5. Click on Value to Upload the File

For example:

how to upload and download files with rest

Finally, click Send to upload the File from Postman.

2) Using AngularJS to upload Files

Firstly, create the following index.jsp page which uses an AngularJS Controller to interact with our REST Service:

<html>
<head>
 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
 <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen" />

<script type = "text/javascript">
	angular.module('app', [])
	.controller('ctrl', function ($scope, $http) {
		$scope.files = [];

		$scope.loadData = function () {
			$http({
				method: 'GET',
				url: '/rest-file-manager/rest/file/list'
			}).then(function successCallback(response) {
				$scope.files = response.data;
			}, function errorCallback(response) {
				console.log(response.statusText);
			});
		}

		$scope.downloadFile = function (filename) {

			$http({
				method: 'GET',
				url: '/rest-file-manager/rest/file/download',
				params: {
					file: filename
				},
				responseType: 'arraybuffer'

			}).success(function (data, status, headers) {

				headers = headers();

				var contentType = headers['content-type'];
				var linkElement = document.createElement('a');

				try {

					var blob = new Blob([data], {
						type: contentType
					});

					var url = window.URL.createObjectURL(blob);

					linkElement.setAttribute('href', url);
					linkElement.setAttribute("download", filename);

					var clickEvent = new MouseEvent("click", {

						"view": window,
						"bubbles": true,
						"cancelable": false

					});

					linkElement.dispatchEvent(clickEvent);

				} catch (ex) {
					console.log(ex);
				}

			}).error(function (data) {
				console.log(data);
			});

		};

		//call loadData when controller initialized
		$scope.loadData();
	})
</script>

<title>REST Download demo with AngularJS</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body ng-app='app' ng-controller='ctrl'>
    <h1>REST Upload/Download demo using AngularJS</h2>
	<h2>Upload file</h2>
	<form method="post" action="rest/file/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>Download file</h2>

        <div class="divTable blueTable">
                <div class="divTableHeading">
                    <div  class="divTableHead">File Name</div>
                    <div  class="divTableHead">Action</div>
                </div>
                <div class="divTableRow" ng-repeat="file in files">
                    <div class="divTableCell">{{ file }}</div>
                    <div class="divTableCell"><a ng-click="downloadFile( file )" class="myButton">Download</a> </div>
                </div>
            </div>
</body>
</html>

Next, deploy the example application using:

mvn clean install wildfly:deploy

Finally, here is how our Rest Endpoint in action after we have uploaded an example XML file:

rest file download demo

A simple JSP / HTML File Manager

In order to upload and download files you can also use a minimalist approach: an HTML page which contains JSP code. The JSP part is mainly for displaying the list of Files available on the Server:

 
       <%@page import="java.io.*, com.mastertheboss.rest.Config" %>
        <%@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/file/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/file/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(Config.UPLOAD_FOLDER, 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>

Here is our simple JSP front-end in action:

rest file upload download demo resteasy

Adding a JUnit 5 Test class to test REST upload

Required: Know about JUnit framework. Check this tutorial to learn more: Getting started with JUnit 5

Finally, we include in this tutorial sample a JUnit 5 Jupiter Test class which shows the hardest part of it: managing MultiPart requests to upload a File with REST Easy Client API:

public class UploadTest {

    String FILENAME="test-file.txt";
    @Test
    public void sendFile() throws Exception {
         
        Client client = ClientBuilder.newClient();

    	WebTarget target = client.target("http://localhost:8080/rest-file-manager/rest/file/upload");
    	
    	MultipartFormDataOutput mdo = new MultipartFormDataOutput();

    	createFile();

    	File filePath = new File(FILENAME);
    	
    	// Check that file exists

    	assertTrue(filePath.exists());
    	
    	mdo.addFormData("attachment", new FileInputStream(filePath),
    		    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);
    	
    	 
}

    private void createFile() {
        try {
            PrintWriter writer = new PrintWriter(FILENAME, "UTF-8");
            writer.println("Some text");
            writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

}

You can run it from the IDE (or using the ‘mvn test’ from the Command Line:

mvn test

Testing the Rest Service sample cURL

Lastly, we will mention that you can also test Files uploading as well with cURL. Assuming that you have a file /tmp/file.txt in place, you can upload the file as follows:

curl -H "attachment: /tmp/file.txt" -F "attachment=@/tmp/file.txt" http://localhost:8080/rest-file-manager/rest/file/upload

Then, you can download the file from cURL as follows:

$ curl -o file.txt http://localhost:8080/rest-file-manager/rest/file/download?file=file.txt

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100     8  100     8    0     0   2555      0 --:--:-- --:--:-- --:--:--  2666

Source code of this Rest service tutorial

You can find the source code for this rest service tutorial here: https://github.com/fmarchioni/mastertheboss/tree/master/javaee/rest-file-manager .

Spring Boot users? Check this tutorial to learn how to manage Files Upload and Download using Spring Boot.