spring:
servlet:
multipart:
# Enable multipart uploads
enabled: true
# Threshold after which files are written to disk.
file-size-threshold: 2KB
# Max file size
max-file-size: 15MB
# Max request size
max-request-size: 20MB
application:
file:
## File Storage Properties
# All files uploaded will be stored in this directory
upload-dir: /home/[user]/files
@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)
public class ApplicationProperties {
public File file;
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public static class File {
public String uploadDir;
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
}
}
@SpringBootApplication
@EnableConfigurationProperties({ApplicationProperties.class})
public class Application {
// ...
}
public class UploadFileResponse {
private String fileName;
private String fileDownloadUri;
private String fileType;
private long size;
public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) {
this.fileName = fileName;
this.fileDownloadUri = fileDownloadUri;
this.fileType = fileType;
this.size = size;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileDownloadUri() {
return fileDownloadUri;
}
public void setFileDownloadUri(String fileDownloadUri) {
this.fileDownloadUri = fileDownloadUri;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
}
public class FileStorageException extends RuntimeException {
public FileStorageException(String message) {
super(message);
}
public FileStorageException(String message, Throwable cause) {
super(message, cause);
}
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class FileNotFoundException extends RuntimeException {
public FileNotFoundException(String message) {
super(message);
}
public FileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
@Service
public class FileStorageService implements FileStorageService {
private final Path fileStorageLocation;
@Autowired
public FileStorageServiceImpl(ApplicationProperties applicationProperties) {
this.fileStorageLocation = Paths.get(applicationProperties.getFile().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(Objects.requireNonNull(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);
}
}
@Override
public boolean deleteFile(String fileName) throws IOException {
Resource resource = loadFileAsResource(fileName);
return resource.getFile().delete();
}
}
@RestController
@RequestMapping("/api/files")
public class FileResource {
private static final Logger logger = LoggerFactory.getLogger(FileResource.class);
private final FileStorageService fileStorageService;
public FileResource(FileStorageService fileStorageService) {
this.fileStorageService = fileStorageService;
}
@PostMapping("/uploadFile")
public ResponseEntity<UploadFileResponse> uploadFile(@RequestParam("file") MultipartFile file) {
UploadFileResponse resp = saveFile(file);
return ResponseEntity.created(URI.create( resp.getFileDownloadUri() ))
.body(resp);
}
private UploadFileResponse saveFile(@RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(fileName)
.toUriString();
return new UploadFileResponse(fileName, fileDownloadUri,
file.getContentType(), file.getSize());
}
@PostMapping("/uploadMultipleFiles")
public ResponseEntity<List<UploadFileResponse>> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
List<UploadFileResponse> resp = Arrays.stream(files)
.map(this::saveFile)
.collect(Collectors.toList());
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.toUriString();
return ResponseEntity.created(URI.create( fileDownloadUri ))
.body(resp);
}
@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);
}
@DeleteMapping("/{fileName:.+}")
public ResponseEntity<Boolean> deleteFile(@PathVariable String fileName, HttpServletRequest request) {
try {
boolean resp = fileStorageService.deleteFile(fileName);
return ResponseEntity.ok()
.body(resp);
} catch (IOException e) {
throw new InternalServerErrorException("");
}
}
}