In this tutorial, we will learn how to upload and download a file using Spring Boot RESTful API. Uploading and downloading files are very common tasks for which developers need to write code in their applications.
This Spring Boot project is upgraded to Spring Boot 3 and Java 17.
Learn and master in Spring boot at Spring Boot Tutorial
We’ll first build the REST APIs for uploading and downloading files, and then test those APIs using Postman.
The source code of this tutorial available on my GitHub Repository.
Let’s get started.
Table of Contents
- Tools and Technologies Used
- Create and Import Spring Boot Project
- Project Directory Structure
- The pom.xml File
- Configuring Server and File Storage Properties
- Automatically binding properties to a POJO class
- Writing APIs for File Upload and Download
- Service for Storing Files in the FileSystem and retrieving them
- Custom Exception Classes
- Running the Application and Testing the APIs via Postman
1. Tools and Technologies Used
- Spring Boot 3+
- JDK - 17 or later
- Spring Framework - 6+
- Maven - 3.2+
- IDE - Eclipse or Spring Tool Suite (STS)
2. Create and Import Spring Boot Project
There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializr at http://start.spring.io/, which is an online Spring Boot application generator.
Let'specify the following details:
- Generate: Maven Project
- Java Version: 17 (Default)
- Spring Boot: 3.0.4
- Group: net.javaguides.springboot
- Artifact: springboot-upload-download-file-rest-api-example
- Name: sspringboot-upload-download-file-rest-api-example
- Description: springboot-upload-download-file-rest-api-example
- Package Name : net.guides.springboot.springbootfileupload
- Packaging: jar (This is the default value)
- Dependencies: Web
Once, all the details are entered, click on the Generate Project button will generate a spring boot project then download it. That’s it! You may now unzip the downloaded application archive and import it into your favorite IDE.
3. Project Directory Structure
Below, the diagram shows a project structure for reference:
4. The pom.xml File
<?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>net.javaguides.springboot</groupId>
<artifactId>springboot-upload-download-file-rest-api-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-upload-download-file-rest-api-example</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.4</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5. Configuring Server and File Storage Properties
Let’s configure our Spring Boot application to enable Multipart file uploads, and define the maximum file size that can be uploaded. We’ll also configure the directory into which all the uploaded files will be stored.
Open src/main/resources/application.properties file, and add the following properties to it -
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB
## File Storage Properties
file.upload-dir=./uploads
server.port=8081
6. Automatically binding properties to a POJO class
Spring Boot has an awesome feature called @ConfigurationProperties using which you can automatically bind the properties defined in the application.properties file to a POJO class.
Let’s define a POJO class called FileStorageProperties inside net.javaguides.springboot.fileuploaddownload package to bind all the file storage properties -package net.javaguides.springboot.fileuploaddownload.property;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadDir;
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
}
7. Writing APIs for File Upload and Download
7.1 File Upload Rest API
Let’s now write the REST APIs for uploading single as well as multiple files. Create a new controller class called FileUploadController inside net.javaguides.springboot.fileuploaddownload.controller package and add following code to it:package net.javaguides.springboot.fileuploaddownload.controller;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import net.javaguides.springboot.fileuploaddownload.payload.Response;
import net.javaguides.springboot.fileuploaddownload.service.FileStorageService;
@RestController
public class FileUploadController {
@Autowired
private FileStorageService fileStorageService;
@PostMapping("/uploadFile")
public Response uploadFile(@RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(fileName)
.toUriString();
return new Response(fileName, fileDownloadUri,
file.getContentType(), file.getSize());
}
@PostMapping("/uploadMultipleFiles")
public List < Response > uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
return Arrays.asList(files)
.stream()
.map(file - > uploadFile(file))
.collect(Collectors.toList());
}
}
7.2 File Download Rest API
Let’s now write the REST API for downloading a file. Create a new controller class called FileDownloadController inside net.javaguides.springboot.fileuploaddownload.controller package and add following code to it:package net.javaguides.springboot.fileuploaddownload.controller; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import net.javaguides.springboot.fileuploaddownload.service.FileStorageService; @RestController public class FileDownloadController { private static final Logger logger = LoggerFactory.getLogger(FileDownloadController.class); @Autowired private FileStorageService fileStorageService; @GetMapping("/downloadFile/{fileName:.+}") public ResponseEntity < Resource > downloadFile(@PathVariable String fileName, HttpServletRequest request) { // Load file as Resource Resource resource = fileStorageService.loadFileAsResource(fileName); // Try to determine file's content type String contentType = null; try { contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath()); } catch (IOException ex) { logger.info("Could not determine file type."); } // Fallback to the default content type if type could not be determined if (contentType == null) { contentType = "application/octet-stream"; } return ResponseEntity.ok() .contentType(MediaType.parseMediaType(contentType)) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"") .body(resource); } }
7.3 Create Response
This Response class used to return responses from the /uploadFile and /uploadMultipleFiles APIs.Let's create Response class inside net.javaguides.springboot.fileuploaddownload.payload package with the following contents:
public class Response { private String fileName; private String fileDownloadUri; private String fileType; private long size; public Response(String fileName, String fileDownloadUri, String fileType, long size) { this.fileName = fileName; this.fileDownloadUri = fileDownloadUri; this.fileType = fileType; this.size = size; } // Getters and Setters (Omitted for brevity) }
8. Service for Storing Files in the FileSystem and retrieving them
Let’s now write the service for storing files in the file system and retrieving them.
Let's create a new class called FileStorageService.java inside net.javaguides.springboot.fileuploaddownload.service with the following contents -
package net.javaguides.springboot.fileuploaddownload.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.nio.file.StandardCopyOption;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import net.javaguides.springboot.fileuploaddownload.exception.FileStorageException;
import net.javaguides.springboot.fileuploaddownload.exception.FileNotFoundException;
import net.javaguides.springboot.fileuploaddownload.property.FileStorageProperties;
@Service
public class FileStorageService {
private final Path fileStorageLocation;
@Autowired
public FileStorageService(FileStorageProperties fileStorageProperties) {
this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir())
.toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
}
}
public String storeFile(MultipartFile file) {
// Normalize file name
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try {
// Check if the file's name contains invalid characters
if (fileName.contains("..")) {
throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
}
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = this.fileStorageLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
}
}
public Resource loadFileAsResource(String fileName) {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
return resource;
} else {
throw new FileNotFoundException("File not found " + fileName);
}
} catch (MalformedURLException ex) {
throw new FileNotFoundException("File not found " + fileName, ex);
}
}
}
9. Custom Exception Classes
The FileStorageService class throws some exceptions in case of unexpected situations. The following are the definitions of those exception classes (All the exception classes go inside the package net.javaguides.springboot.fileuploaddownload.exception).
FileNotFoundException
package net.javaguides.springboot.fileuploaddownload.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class FileNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1 L;
public FileNotFoundException(String message) {
super(message);
}
public FileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
FileStorageException
package net.javaguides.springboot.fileuploaddownload.exception;
public class FileStorageException extends RuntimeException {
private static final long serialVersionUID = 1 L;
public FileStorageException(String message) {
super(message);
}
public FileStorageException(String message, Throwable cause) {
super(message, cause);
}
}
To know more about exception handling in Spring boot then check out Spring Boot 2 Exception Handling for REST APIs
Note that, we’ve annotated the above exception class with @ResponseStatus(HttpStatus.NOT_FOUND). This ensures that Spring boot responds with a 404 Not Found status when this exception is thrown.
10. Running the Application and Testing the APIs via Postman
We’re done developing our backend APIs. Let’s run the application and test the APIs via Postman. Type the following command from the root directory of the project to run the application -
mvn spring-boot:run
Once started, the application can be accessed at http://localhost:8081
1. Upload File
2. Upload Multiple Files
3. Download File
Conclusion
That's it. We learned how to upload single as well as multiple files via REST APIs written in Spring Boot. We also learned how to download files in Spring Boot.
I hope the post was helpful to you. You can download the entire code for the project that we built in this article from the GitHub repository.
Can I write down the source of the blog and post it on my technical blog?
ReplyDelete