How to build SpringBoot RestAPIs and Ajax client to Upload Download Multiple Files in one of the most common question in the development world? So in the tutorial, I will guide step by step how to do it by examples with clearly and running sourcecode.
Related posts:
- Spring Boot Security JWT Authentication Example – MySQL/PostgreSQL + Spring JPA + RestAPIs
- Angular Spring Boot JWT Authentication Example – Angular 6, 8, 9 + Spring Security + MySQL/PostgreSQL
- SpringBoot RestAPIs Upload Download Multiple CSV files to MySQL/PostgreSQL with Ajax + RestClient
What will we do in the post?
- Overview
- Implement File Service
- Design Response Message
- Implement Upload Controller RestAPI
- Implement Download Controller RestAPI
- Implement Global Exception Handler
- Test RestAPIs
- Create Upload Download Html Views
- Implement Ajax Upload Download Files
- Testing Ajax Upload/Download Client
- Sourcecode
Now let’s go!
Overview
We create a SpringBoot RestAPIs project to Upload and Download single/multiple files with 2 main block:
- Backend block: implements Upload/Download RestAPI controllers that uses File Service to store or retrieve files from file system then returns back a http response message to Ajax Rest Client
- Frontend block: creates an Ajax Rest Client to upload and download single and multiple files from the SpringBoot RestAPIs

We create a SpringBoot project with below structure to implement about design:

-
controller
package implements Upload and Download RestAPIs controllers -
service
package implements specific functions to store and retrieve file systems that will be used by Upload and Download Controllers -
message
package defines a needed information in a response object to return back to Ajax Rest Client. -
errorhandler
package implements a global restapi exception handler. -
index.html
html file defines upload and download views. -
uploaddownloadfiles.js
implements Ajax functions to upload and download files from SpringBoot RestAPIs.
For the tutorial, we use Eclipse integrated with SpringToolSuite to create a SpringBoot project with a spring-boot-starter-web
dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Implement File Service
We define a FileService
interface to interact (save/retrieve files) with File System. Here is the details sourcecode:
package com.loizenai.uploaddownloadfiles.service;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
/**
* Copyright by https://loizenai.com
* @author loizenai.com
*
*/
public interface FileService {
public void store(MultipartFile file);
public Resource loadFile(String filename);
public void deleteAll();
public void init();
public Stream<Path> getFiles();
}
-
store(MultipartFile file)
is used to save a file to disk -
Resource loadFile(String filename)
is used to retrieve a file from disk -
voide deleteAll()
is used to delete all file in a upload folder. -
void init()
is used to create a upload folder
We implement above File Service APIs in FileServiceImpl.java
class:
package com.loizenai.uploaddownloadfiles.service;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* Copyright by https://loizenai.com
* @author loizenai.com
*
*/
@Service
public class FileServiceImpl implements FileService {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("storages");
@Override
public void store(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("Error! -> message = " + e.getMessage());
}
}
@Override
public Resource loadFile(String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("FAIL!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("Error! -> message = " + e.getMessage());
}
}
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
@Override
public void init() {
try {
Files.createDirectory(rootLocation);
} catch (IOException e) {
throw new RuntimeException("Could not initialize file storage!");
}
}
@Override
public Stream<Path> getFiles() {
try {
return Files.walk(this.rootLocation, 1).filter(path -> !path.equals(this.rootLocation))
.map(this.rootLocation::relativize);
} catch (IOException e) {
throw new RuntimeException("Error! -> message = " + e.getMessage());
}
}
}
Design Response Message
I define a Response
class to contain necessary information to return back to rest Api client. In the tutorial, I define a Response
class with 4 attributes:
-
messages
attribute contains a list information of upload proccessing of each file (ok
orfail
) -
fileInfos
attribute contains a list information of each uploaded files. -
error
attribute contains detail information of an error if having any exception which catched by Globle RestAPI Exceptions when do uploading files.
public class Response {
private List<Message> messages = null;
private List<FileInfo> fileInfos = null;
private Error error = null;
private String errStatus = "";
public Response() {
this.messages = new ArrayList<Message>();
}
public Response(List<FileInfo> fileInfos) {
this.fileInfos = fileInfos;
}
public Response(String errStatus, Error err) {
this.errStatus = errStatus;
this.error = err;
}
//...
Implement Upload RestAPI Controller
We define a UploadController.java
class to provide uploading RestAPIs with 2 specific usercases:
- Upload a single file restapi
- Upload multiple files restapi

Upload Single File RestAPIs
In the UploadController.java
class, we define a method uploadSingleFile(@RequestParam("uploadfile") MultipartFile uploadfile)
to handle any http POST request with URL /api/upload/file/single
for uploading a single file:
@PostMapping("/single")
public Response uploadSingleFile(@RequestParam("uploadfile") MultipartFile uploadfile) {
Response response = new Response();
// Checking the upload-file before processing
if(uploadfile.getOriginalFilename().isEmpty()) {
response.addMessage(new Message(uploadfile.getOriginalFilename(),
"No selected file to upload! Please do the checking", "fail"));
return response;
}
try {
// save file to disk
fileService.store(uploadfile);
response.addMessage(new Message(uploadfile.getOriginalFilename(), "Upload Successfully!", "ok"));
} catch(Exception e) {
response.addMessage(new Message(uploadfile.getOriginalFilename(), e.getMessage(), "fail"));
}
return response;
}
Upload Multiple Files RestAPIs
In the UploadController.java
class, we define a method uploadMultipleFiles(@RequestParam("uploadfiles") MultipartFile[] uploadfiles)
to handle any http POST request to URL /api/upload/file/multiple
for uploading multiple files. Here is a detail:
@PostMapping("/multiple")
public Response uploadMultipleFiles(@RequestParam("uploadfiles") MultipartFile[] uploadfiles) {
Response response = new Response();
/*
* Filtering files had been selected for uploading (the files having names)
*/
MultipartFile[] readyUploadedFiles = Arrays.stream(uploadfiles).filter(
x -> !StringUtils.isEmpty(x.getOriginalFilename())).toArray(MultipartFile[]::new);
/*
* Checking whether having at least one file had been selected for uploading
*/
if (readyUploadedFiles.length == 0) {
response.addMessage(new Message("", "No selected file to upload!", "fail"));
return response;
}
/*
* Do the uploading
*/
for (MultipartFile file : readyUploadedFiles) {
try {
fileService.store(file);
response.addMessage(new Message(file.getOriginalFilename(), "Upload Successfully!", "ok"));
} catch (Exception e) {
response.addMessage(new Message(file.getOriginalFilename(), e.getMessage(), "fail"));
}
}
return response;
}
Implement Download Controller RestAPI
We create a class DownloadController.java
to download uploaded files with 2 methods:
getListFiles(Model model)
is used to list all uploaded files' namedownloadFile(@PathVariable String filename)
is used to download a file with a specific name

package com.loizenai.uploaddownloadfiles.controller;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import com.loizenai.uploaddownloadfiles.message.FileInfo;
import com.loizenai.uploaddownloadfiles.message.Response;
import com.loizenai.uploaddownloadfiles.service.FileService;
@RestController
@RequestMapping("/api/download")
public class DownloadController {
@Autowired
FileService fileService;
/*
* Retrieve Files' Information
*/
@GetMapping("/files")
public Response getListFiles(Model model) {
List<FileInfo> files = fileService.getFiles().map(path -> {
String filename = path.getFileName().toString();
String url = MvcUriComponentsBuilder
.fromMethodName(DownloadController.class, "downloadFile", path.getFileName().toString()).build()
.toString();
return new FileInfo(filename, url);
}).collect(Collectors.toList());
return new Response(files);
}
/*
* Download Files
*/
@GetMapping("/file/{filename}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
MediaType contentType = MediaType.IMAGE_PNG;
String extension = filename.split("\\.")[1];
switch(extension) {
case "txt": contentType = MediaType.TEXT_PLAIN;
break;
case "xlsx": contentType = MediaType.parseMediaType("application/vnd.ms-excel");
break;
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=" + filename);
return ResponseEntity.ok().headers(headers)
.contentType(contentType)
.body(fileService.loadFile(filename));
}
}
Implement Global Exception Handler
If an upload file has an enough big size, Springboot application will throw an exception. How to handle the exception? We need to define an RestExceptionHandler
class that extends ResponseEntityExceptionHandler
and use an annotation @ControllerAdvice
to handle the big size issue.
package com.loizenai.uploaddownloadfiles.errorhandler;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import com.loizenai.uploaddownloadfiles.message.Error;
import com.loizenai.uploaddownloadfiles.message.Response;
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
// Catch file size exceeded exception!
@SuppressWarnings({ "rawtypes", "unchecked" })
@ExceptionHandler(MultipartException.class)
@ResponseBody
ResponseEntity<Response> handleControllerException(HttpServletRequest request, Throwable ex) {
HttpStatus status = getStatus(request);
return new ResponseEntity(new Response("error", new Error("0x123", ex.getMessage())), status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}
}
Test RestAPIs
We define a scenario with 5 testcases to test the implemented RestAPIs:
- Upload single file
- Upload multiple files
- Get all uploaded files' names
- Download a file with a specific name
Upload single file testcase
Make a http POST request to URL http://localhost:8080/api/upload/file/single
to upload a single file:

Reponse:

Upload multiple files test case
Make a http POST request to URL http://localhost:8080/api/upload/file/multiple
to upload multiple files:

Response:

Get all uploaded files' name testcase
Make a http GET request to URL http://localhost:8080/api/download/files
to get all uploaded files' names

Response:

Download a file with a specific name
Make a http GET request to URL http://localhost:8080/api/download/file/loizenai-tutorial.txt
to download a uploaded file with a specific name loizenai-tutorial.txt
:

Response - Successfully:

Create Upload Download Html Views
We use Bootstrap framework to define a Upload Download Html View with 3 separated parts:
- a form to upload single file
- a form to upload multiple files
- a div to get all uploaded files

<!DOCTYPE html>
<html lang="en">
<head>
<title>Upload Download File Examples</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/js/uploaddownloadfiles.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-sm-7" style="background-color:#e6fffa; padding:10px; border-radius:3px">
<h3>Upload Single File</h3>
<form id="uploadSingleFileForm">
<div class="form-group">
<label class="control-label" for="uploadfile">Choose a File:</label>
<input type="file" class="form-control"
placeholder="Choose a upload file" name="uploadfile" required></input>
</div>
<button type="submit" class="btn btn-danger" id="btnUploadSingleFileSubmit">Submit</button>
</form>
<div id="response" style="display:none">
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-sm-7" style="background-color:#e6fffa; padding:10px; border-radius:3px">
<h3>Upload Multiple Files</h3>
<form id="uploadMultipleFilesForm">
<div class="form-group">
<label class="control-label" for="uploadfiles">Choose Files:</label>
<input type="file" class="form-control"
placeholder="Choose upload files" name="uploadfiles" multiple required></input>
</div>
<button type="submit" class="btn btn-danger" id="btnUploadMultipleFilesSubmit">Submit</button>
</form>
<div id="responses" style="display:none">
</div>
</div>
</div>
<hr>
<div id="uploadedfiles" class="row">
<div class="col-sm-7">
<button type="button" class="btn btn-primary" id="btnGetFiles">All Files</button>
<div id="uploadedfiles">
</div>
</div>
</div>
</div>
</body>
</html>
Implement Ajax Upload Download Files
We use JQuery Ajax to submit html upload file forms and get a list names of uploaded files:
/**
* Copyright by https://loizenai.com
* Author: loizenai.com
*/
$(document).ready(function() {
/**
* Upload single file to SpringBoot
* at RestAPI: /api/upload/file/single
*/
$("#uploadSingleFileForm").submit(function(evt) {
evt.preventDefault();
var formData = new FormData($(this)[0]);
$.ajax({
url : '/api/upload/file/single',
type : 'POST',
data : formData,
async : false,
cache : false,
contentType : false,
enctype : 'multipart/form-data',
processData : false,
success : function(response) {
$("#response").empty();
if(response.errStatus !== "error"){
var displayInfo = response.messages[0].filename + " : " + response.messages[0].message + "<br>";
$("#response").append(displayInfo);
var downloadLink = "http://localhost:8080/api/download/file/" + response.messages[0].filename;
var downloadAt = " -> Download File: " + "<a href=" + "\'" + downloadLink + "\'>" + downloadLink + "</a>";
$("#response").append(downloadAt);
// add some css
$("#response").css("display", "block");
$("#response").css("background-color", "#e6e6ff");
$("#response").css("border", "solid 1px black");
$("#response").css("border-radius", "3px");
$("#response").css("margin", "10px");
$("#response").css("padding", "10px");
}else{
$("#response").css("display", "none");
var error = response.error.errDesc;
alert(error);
}
},
error: function(e){
alert("Fail! " + e);
}
});
return false;
});
/**
* Upload Multiple Files to SpringBoot RestAPI
*/
$("#uploadMultipleFilesForm").submit(function(evt) {
evt.preventDefault();
var formData = new FormData($(this)[0]);
$.ajax({
url : '/api/upload/file/multiple',
type : 'POST',
data : formData,
async : false,
cache : false,
contentType : false,
enctype : 'multipart/form-data',
processData : false,
success : function(response) {
$("#responses").empty();
if(response.errStatus !== "error"){
var displayInfo = "<ul>";
for(var i=0; i<response.messages.length; i++){
displayInfo += "<li>" + response.messages[i].filename + " : " + response.messages[i].message;
if (response.messages[i].status === "ok") {
var downloadLink = "http://localhost:8080/api/download/file/" + response.messages[i].filename;
var downloadAt = " -> Link: " + "<a href=" + "\'"
+ downloadLink + "\'>" + downloadLink + "</a>";
displayInfo += "<br>" + downloadAt;
}
displayInfo += "</li>";
}
$("#responses").append(displayInfo + "</ul>");
$("#responses").css("display", "block");
// add some css
$("#responses").css("background-color", "#e6e6ff");
$("#responses").css("border", "solid 1px black");
$("#responses").css("border-radius", "3px");
$("#responses").css("margin", "10px");
$("#responses").css("padding", "10px");
}else{
$("#responses").css("display", "none");
var error = response.error.errDesc;
alert(error);
}
},
error: function(e){
alert("Fail! " + e);
}
});
return false;
});
/**
* Get all uploaded files and download-links
*/
$( "#btnGetFiles").click(function() {
$.get('/api/download/files', function (response, textStatus, jqXHR) { // success callback
var files = "<ul>";
for(var i=0; i<response.fileInfos.length; i++) {
files += "<li>" + response.fileInfos[i].filename + "<br>"
+ " Link -> <a href=" + "\'" + response.fileInfos[i].url + "\'" + ">"
+ response.fileInfos[i].url
+ "</a></li>"
}
files += "</ul>";
$("#uploadfiles").append(files);
// add some css
$("#uploadfiles").css("background-color", "#e6e6ff");
$("#uploadfiles").css("border", "solid 1px black");
$("#uploadfiles").css("border-radius", "3px");
$("#uploadfiles").css("margin", "10px");
$("#uploadfiles").css("padding", "10px");
});
});
})
Testing Ajax Upload/Download Client
- Upload single file:

- Upload multiple files:

- Get a list of uploaded files' name and download them:

Sourcecode
All features of sourcecode for the tutorial SpringBoot Upload Download Multiple Files with Rest API + Ajax:
- Backend
- Upload Single File RestAPI
- Upload Multiple Files RestAPI
- Get All Uploaded Files' Names
- Download a Uploaded File with a specific name
- Frontend
- Html Upload Single File Form
- Html Upload Multiple Files Form
- JQuery Ajax to get a list names of uploaded files
- GitHub Sourcecode:
Exception post! thx so much
Please upload more post about SpringBoot and Ajax for Upload file!
So big thx
So hard for starting! But I can do it now! Thx man
I understand cleary about SpringBoot Upload file! Thxs
Thank so much for great upload file with SpringBoot and Ajax fetching
Thank man for excellent post! Hope more article man!
You are rock programming
Thnk pro
Good job!!!
Good job man!