In any application development, file upload is a feature that a developer cannot discard. In this post, I am going to share how to implement file upload in RESTFul Spring Boot application.
How do I upload files to Spring Boot REST?
Before getting into the main content, I would like to share that you can find other posts in the field of Spring Boot here.
Kindly note that, in the code sample that I will be sharing here, I have used Lombok annotations to generate getters and setters, and constructors.
Spring Boot Rest APIs for uploading Files
RESTFul Controller for File Upload in Spring Boot
First, let us create resource mappings/endpoints that we need typically in file uploads.
The first endpoint is for uploading the file. The file upload method takes in a parameter of MultipartFile
.
The second endpoint is for getting all the files inside the directory.
The third endpoint is for getting a file by file name as the search keyword.
The last endpoint is for deleting a by based on the input parameter (path of the file) submitted by the consumer.
package com.bhutanio.learningspringboot;
import lombok.AllArgsConstructor;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@AllArgsConstructor
@CrossOrigin
@RequestMapping(value = "/files")
public class FileUploadController {
private final FileUploadService fileUploadService;
@PostMapping("/upload")
public ResponseEntity<ResponseMessage> uploadFile(@RequestParam("file") MultipartFile file) {
String message = "";
try {
fileUploadService.save(file);
message = "Uploaded the file successfully: " + file.getOriginalFilename();
return ResponseEntity.status( HttpStatus.OK)
.body(new ResponseMessage(message));
} catch (Exception e) {
message = "Could not upload the file: " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED)
.body(new ResponseMessage(message));
}
}
@GetMapping("/getfiles")
public ResponseEntity<List<FileInfo>> getListFiles() {
List<FileInfo> fileInfos = fileUploadService.loadAll().map(path -> {
String filename = path.getFileName().toString();
String url = MvcUriComponentsBuilder
.fromMethodName(FileUploadController.class, "getFile", path.getFileName().toString()).build().toString();
return new FileInfo(filename, url);
}).collect( Collectors.toList());
return ResponseEntity.status(HttpStatus.OK).body(fileInfos);
}
@GetMapping("/files/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> getFile(@PathVariable String filename) {
Resource file = fileUploadService.load(filename);
return ResponseEntity.ok()
.header( HttpHeaders
.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
@PostMapping("/delete")
public ResponseEntity<ResponseMessage> deleteFile(@RequestParam("path") String filename) {
String message = "";
try {
fileUploadService.deleteByPath(filename);
message = "Deleted the file successfully: " + filename;
return ResponseEntity.status( HttpStatus.OK).body(new ResponseMessage(message));
} catch (Exception e) {
message = "Could not delete the file: " + filename + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
}
}
}
Create Service Interface
Let us create services for the endpoints we created above. Here is the interface that we’ll be using in the implementation of the service.
package com.bhutanio.learningspringboot;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Path;
import java.util.stream.Stream;
public interface FileUploadService {
public void init();
public void save(MultipartFile file);
public Resource load(String filename);
public void deleteAll();
public void deleteByPath(String path);
public Stream<Path> loadAll();
}
Create Service Implementation
Here is the class that implements
the service interface that we created above. We will be annotating this class with @Service
.
package com.bhutanio.learningspringboot;
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;
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;
@Service
public class FilesStorageServiceImpl implements FileUploadService{
private final Path root = Paths.get("C:/storage/");
@Override
public void init() {
try {
if(!Files.exists( root )) Files.createDirectory(root);
} catch (IOException e) {
throw new RuntimeException("Could not initialize folder for upload!");
}
}
@Override
public void save(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.root.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("Could not store the file. Error: " + e.getMessage());
}
}
@Override
public Resource load(String filename) {
try {
Path file = root.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("Could not read the file!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("Error: " + e.getMessage());
}
}
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(root.toFile());
}
@Override
public void deleteByPath(String filename) {
try{
Path file = root.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
Files.delete( ( file ));
} else {
throw new RuntimeException("Could not read the file!");
}}catch (Exception ex){
}
}
@Override
public Stream<Path> loadAll() {
try {
return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
} catch (IOException e) {
throw new RuntimeException("Could not load the files!");
}
}
}
NOTE: In the code above, I have set C:/storage/
as my root folder for file uploads. You can change as per your requirements.
Creating Models
In the sample codes I have shown above, I have used two models – FileInfo
and ResponseMessage
.
As stated earlier, in these two models you will not see getters
, setters
and constructors
. This is because I have used Lombok annotations for creating them.
Model for file information
package com.bhutanio.learningspringboot;
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class FileInfo {
private String name;
private String url;
}
Model for response messages
package com.bhutanio.learningspringboot;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ResponseMessage {
private Integer status;
private String text;
private String responseText;
public ResponseMessage(String text) {
this.text = text;
}
}
Initiating File Upload Service on project startup
To initiate the file upload service on the project startup, we will inject FileUploadService
with @Resource
annotation.
The main class implements CommandLineRunner
.
package com.bhutanio.learningspringboot;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.Resource;
@SpringBootApplication
public class LearningSpringBootApplication implements CommandLineRunner {
@Resource
FileUploadService fileUploadService;
public static void main(String[] args) {
SpringApplication.run(LearningSpringBootApplication.class, args);
}
@Override
public void run(String... arg) throws Exception {
fileUploadService.deleteAll();
fileUploadService.init();
}
}
On the project startup, deleteAll() method will be executed deleting all the files in the upload directory because of this line:
fileUploadService.deleteAll();
If you do not want this to happen, you can discard this line of code.
The following line of code will assist in initializing the file upload – creates the folder if it is not found or return a message of failure if it cannot be created. Here is the code snippet that performs this:
public void init() {
try {
if(!Files.exists( root )) Files.createDirectory(root);
} catch (IOException e) {
throw new RuntimeException("Could not initialize folder for upload!");
}
}
The next post will be RESTFul file upload with progress bar in Angular framework.