commit dbb7f1ad0014a63e8b54dabba8cc29fb28e79e50 Author: takiguchi Date: Sat Mar 7 15:32:55 2020 +0100 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a2a3040 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ + +### VS Code ### +.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2ae6015 --- /dev/null +++ b/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.5.RELEASE + + + com.example + rest-file-api + 0.0.1-SNAPSHOT + demo + Demo project for Spring Boot + + + 11 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + commons-fileupload + commons-fileupload + 1.4 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/src/main/java/com/example/demo/DemoApplication.java b/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..64b538a --- /dev/null +++ b/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/src/main/java/com/example/demo/configuration/MultipartResolverConfiguration.java b/src/main/java/com/example/demo/configuration/MultipartResolverConfiguration.java new file mode 100644 index 0000000..4c6793f --- /dev/null +++ b/src/main/java/com/example/demo/configuration/MultipartResolverConfiguration.java @@ -0,0 +1,19 @@ +package com.example.demo.configuration; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.multipart.MultipartResolver; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; + +import java.nio.file.Path; + +@Configuration +public class MultipartResolverConfiguration { + @Bean + public MultipartResolver multipartResolver(@Value("${app.files.uploads.max-size}") int maxSize) { + CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); + multipartResolver.setMaxUploadSize(maxSize * 1000 * 1000); + return multipartResolver; + } +} diff --git a/src/main/java/com/example/demo/controller/FileController.java b/src/main/java/com/example/demo/controller/FileController.java new file mode 100644 index 0000000..dc8af9b --- /dev/null +++ b/src/main/java/com/example/demo/controller/FileController.java @@ -0,0 +1,73 @@ +package com.example.demo.controller; + +import com.example.demo.payload.UploadFileResponse; +import com.example.demo.service.FileService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +public class FileController { + private static final Logger logger = LoggerFactory.getLogger(FileController.class); + private FileService service; + + public FileController(FileService service) { + this.service = service; + } + + @PostMapping("/upload") + public UploadFileResponse upload(@RequestParam("file") MultipartFile file) { + String fileName = service.store(file); + + String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath() + .path("/downloadFile/") + .path(fileName) + .toUriString(); + + return new UploadFileResponse(fileName, fileDownloadUri, file.getContentType(), file.getSize()); + } + + @PostMapping("/uploadMultipleFiles") + public List uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { + return Arrays.asList(files) + .stream() + .map(file -> upload(file)) + .collect(Collectors.toList()); + } + + @GetMapping("/downloadFile/{fileName:.+}") + public ResponseEntity downloadFile(@PathVariable String fileName, HttpServletRequest request) { + // Load file as Resource + Resource resource = service.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); + } +} diff --git a/src/main/java/com/example/demo/exception/FileStorageException.java b/src/main/java/com/example/demo/exception/FileStorageException.java new file mode 100644 index 0000000..1f6cf65 --- /dev/null +++ b/src/main/java/com/example/demo/exception/FileStorageException.java @@ -0,0 +1,12 @@ +package com.example.demo.exception; + +import java.io.IOException; + +public class FileStorageException extends RuntimeException { + public FileStorageException(String s) { + } + + public FileStorageException(String s, IOException ex) { + + } +} diff --git a/src/main/java/com/example/demo/exception/MyFileNotFoundException.java b/src/main/java/com/example/demo/exception/MyFileNotFoundException.java new file mode 100644 index 0000000..b4f3b9d --- /dev/null +++ b/src/main/java/com/example/demo/exception/MyFileNotFoundException.java @@ -0,0 +1,12 @@ +package com.example.demo.exception; + +import java.net.MalformedURLException; + +public class MyFileNotFoundException extends RuntimeException { + public MyFileNotFoundException(String s, MalformedURLException ex) { + } + + public MyFileNotFoundException(String s) { + + } +} diff --git a/src/main/java/com/example/demo/payload/UploadFileResponse.java b/src/main/java/com/example/demo/payload/UploadFileResponse.java new file mode 100644 index 0000000..e8dd6cd --- /dev/null +++ b/src/main/java/com/example/demo/payload/UploadFileResponse.java @@ -0,0 +1,47 @@ +package com.example.demo.payload; + +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; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/demo/service/FileService.java b/src/main/java/com/example/demo/service/FileService.java new file mode 100644 index 0000000..53d957e --- /dev/null +++ b/src/main/java/com/example/demo/service/FileService.java @@ -0,0 +1,61 @@ +package com.example.demo.service; + +import com.example.demo.exception.FileStorageException; +import com.example.demo.exception.MyFileNotFoundException; +import org.springframework.beans.factory.annotation.Value; +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 java.io.IOException; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +@Service +public class FileService { + + + private Path fileStorageLocation; + + public FileService(@Value("${app.files.uploads.dir}") Path fileStorageLocation) { + this.fileStorageLocation = fileStorageLocation; + } + + public String store(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 = 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 MyFileNotFoundException("File not found " + fileName); + } + } catch (MalformedURLException ex) { + throw new MyFileNotFoundException("File not found " + fileName, ex); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..be1bfe6 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,14 @@ +spring: + servlet: + multipart: + enabled: true + file-size-threshold: 2KB + max-file-size: 2000MB + max-request-size: 2150MB + +app: + files: + uploads: + dir: /home/takiguchi/Developpement/Java/back-ups/demo2/files/uploads + # Max size in MB + max-size: 2000