diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/FileUploadController.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/FileUploadController.java new file mode 100644 index 0000000..83eddb4 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/FileUploadController.java @@ -0,0 +1,75 @@ +package com.javadevjournal.controller; + +import com.javadevjournal.data.FileMetaData; +import com.javadevjournal.exception.FileStorageException; +import com.javadevjournal.service.FileStorageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.servlet.http.HttpServletResponse; +import java.io.FileNotFoundException; + +@Controller +public class FileUploadController extends PageController{ + + @Autowired + FileStorageService fileStorageService; + + /** + * Controller to display the file upload form on the frontend. + * @param model + * @return + */ + @GetMapping("/upload-file") + public String uploadFile(final Model model){ + return "uploadFile"; + } + + /** + * POST method to accept the incoming file in the application.This method is designed to accept + * only 1 file at a time. + * @param file + * @param redirectAttributes + * @return succes page + */ + @PostMapping("/upload-file") + public String uploadFile(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes, Model model){ + + try { + FileMetaData data = fileStorageService.store(file); + data.setUrl(fileDownloadUrl(data.getFileName(),"/media/download/")); + model.addAttribute("uploadedFile", data); + + } catch (FileStorageException e) { + model.addAttribute("error", "Unable to store the file"); + return "uploadFile"; + } + return "uploadFile"; + } + + /** + * Controller to allow customer to download the file by passing the file name as the + * request URL. + * @param fileName + * @param response + * @return + * @throws FileNotFoundException + */ + @GetMapping("/media/download/{fileName:.+}") + public ResponseEntity downloadFIle(@PathVariable String fileName, final HttpServletResponse response) throws FileNotFoundException { + FileMetaData fileData= fileStorageService.getFile(fileName); + response.setContentType(fileData.getMime()); + return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + fileName + "\"").body(fileData.getResource()); + } +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/PageController.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/PageController.java new file mode 100644 index 0000000..d701af9 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/PageController.java @@ -0,0 +1,12 @@ +package com.javadevjournal.controller; + +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +public class PageController { + + public String fileDownloadUrl(final String fileName, final String baseURL){ + return ServletUriComponentsBuilder.fromCurrentContextPath() + .path(baseURL) + .path(fileName).toUriString(); + } +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/RestFileUploadController.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/RestFileUploadController.java new file mode 100644 index 0000000..c2ae810 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/controller/RestFileUploadController.java @@ -0,0 +1,56 @@ +package com.javadevjournal.controller; + +import com.javadevjournal.data.FileMetaData; +import com.javadevjournal.exception.FileStorageException; +import com.javadevjournal.service.FileStorageService; +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.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import java.io.FileNotFoundException; + +@RestController +@RequestMapping("/api/v1") +public class RestFileUploadController extends PageController { + + @Autowired + FileStorageService fileStorageService; + + /** + * REST controller to allow file uploading for our REST API + * @param file + * @param redirectAttributes + * @param model + * @return FileMetaData + * @throws FileStorageException + */ + @PostMapping("/upload-file") + @ResponseBody + public FileMetaData uploadFile(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes, Model model) throws FileStorageException { + FileMetaData data= fileStorageService.store(file); + data.setUrl(fileDownloadUrl(data.getFileName(),"/api/v1/media/download/")); + return data; + } + + /** + * Rest Controller method for file download feature. + * @param fileName + * @return ResponseEntity + * @throws FileNotFoundException + */ + @GetMapping("/media/download/{fileName:.+}") + public ResponseEntity downloadFile(@PathVariable String fileName) throws FileNotFoundException { + FileMetaData fileData= fileStorageService.getFile(fileName); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + fileName + "\"") + .contentType(MediaType.parseMediaType(fileData.getMime())) + .body(fileData.getResource()); + } +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/data/FileMetaData.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/data/FileMetaData.java new file mode 100644 index 0000000..d4340b4 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/data/FileMetaData.java @@ -0,0 +1,62 @@ +package com.javadevjournal.data; + +import org.springframework.core.io.Resource; + +public class FileMetaData { + + private String fileName; + private String url; + private String mime; + private long size; + private Resource resource; + + public FileMetaData() { + } + + public FileMetaData(String fileName, String url, String mime, long size) { + this.fileName = fileName; + this.url = url; + this.mime = mime; + this.size = size; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMime() { + return mime; + } + + public void setMime(String mime) { + this.mime = mime; + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + public Resource getResource() { + return resource; + } + + public void setResource(Resource resource) { + this.resource = resource; + } +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/exception/FileStorageException.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/exception/FileStorageException.java new file mode 100644 index 0000000..727f736 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/exception/FileStorageException.java @@ -0,0 +1,16 @@ +package com.javadevjournal.exception; + +public class FileStorageException extends Exception { + + public FileStorageException() { + super(); + } + + public FileStorageException(String message) { + super(message); + } + + public FileStorageException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/service/DefaultFileStorageService.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/service/DefaultFileStorageService.java new file mode 100644 index 0000000..6f9bee7 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/service/DefaultFileStorageService.java @@ -0,0 +1,132 @@ +package com.javadevjournal.service; + +import com.javadevjournal.data.FileMetaData; +import com.javadevjournal.exception.FileStorageException; +import com.javadevjournal.utils.UploadFileProperties; +import org.apache.tika.detect.Detector; +import org.apache.tika.io.TikaInputStream; +import org.apache.tika.metadata.Metadata; +import org.apache.tika.mime.MediaType; +import org.apache.tika.parser.AutoDetectParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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 java.io.FileNotFoundException; +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 java.util.List; + +@Service("fileStorageService") +public class DefaultFileStorageService implements FileStorageService { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultFileStorageService.class); + + @Autowired + UploadFileProperties uploadFileProperties; + + @Override + public FileMetaData store(MultipartFile file) throws FileStorageException { + + //Normalize the path by suppressing sequences like "path/.." and inner simple dots. + String fileName = StringUtils.cleanPath(file.getOriginalFilename()); + try { + // we can add additional file validation to discard invalid files + Path uploadDir = getUploadDirLocation().resolve(fileName); + + //copy the file to the upload directory,it will replace any file with same name. + Files.copy(file.getInputStream(), uploadDir, StandardCopyOption.REPLACE_EXISTING); + + FileMetaData fileData = new FileMetaData(); + fileData.setFileName(fileName); + fileData.setSize(file.getSize()); + fileData.setMime(file.getContentType()); + return fileData; + + } catch (IOException e) { + LOG.error("unable to cpy file to the target location {}", e); + throw new FileStorageException("Unable to store file " + fileName); + } + } + + /** + * Read all files and return as list of @FileMetaData + * @return + */ + @Override + public List getAllFiles() { + return null; + } + + /** + * Method to return the file as @Resource for the download.It read the file from the file system + * and return it as @Resource + * @param fileName + * @return FileMetaData + * @throws FileNotFoundException + */ + @Override + public FileMetaData getFile(String fileName) throws FileNotFoundException { + Path path = getUploadDirLocation().resolve(fileName).normalize(); + + try { + Resource resource = new UrlResource(path.toUri()); + + if(resource.exists()){ + Metadata metadata = getFileMetaDataInfo(resource); + FileMetaData fileMetaData = new FileMetaData(); + fileMetaData.setResource(resource); + fileMetaData.setFileName(fileName); + fileMetaData.setSize(metadata.size()); + fileMetaData.setMime(metadata.get(Metadata.CONTENT_TYPE)); + return fileMetaData; + } + else{ + throw new FileNotFoundException("Not able to find file"); + } + } catch (MalformedURLException e) { + throw new FileNotFoundException("Not able to find file"); + } + } + + /** + * Provides the upload directory location based on the application.properties configurations + * + * @return Path + */ + private Path getUploadDirLocation() { + return Paths.get(uploadFileProperties.getUploadDir()).toAbsolutePath().normalize(); + } + + /** + * Helper method to get the file meta-data using Apache Tikka corre library.JDK also provide + * way to read meta-data information but it's very limited and have lot of issues. + * @param resource + * @return Metadata + */ + private Metadata getFileMetaDataInfo(Resource resource){ + AutoDetectParser parser = new AutoDetectParser(); + Detector detector = parser.getDetector(); + Metadata metadata = new Metadata(); + try { + metadata.set(Metadata.RESOURCE_NAME_KEY, resource.getFile().getName()); + TikaInputStream stream = TikaInputStream.get(resource.getInputStream()); + MediaType mediaType = detector.detect(stream,metadata); + metadata.set(Metadata.CONTENT_TYPE, mediaType.toString()); + } catch (IOException e) { + e.printStackTrace(); + //fallback to default content type + metadata.set(Metadata.CONTENT_TYPE, "application/octet-stream"); + + } + return metadata; + } +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/service/FileStorageService.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/service/FileStorageService.java new file mode 100644 index 0000000..4fe54ba --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/service/FileStorageService.java @@ -0,0 +1,16 @@ +package com.javadevjournal.service; + +import com.javadevjournal.data.FileMetaData; +import com.javadevjournal.exception.FileStorageException; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileNotFoundException; +import java.nio.file.Path; +import java.util.List; + +public interface FileStorageService { + + FileMetaData store(MultipartFile file) throws FileStorageException; + List getAllFiles(); + FileMetaData getFile(String fileName) throws FileNotFoundException; +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/utils/UploadFileProperties.java b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/utils/UploadFileProperties.java new file mode 100644 index 0000000..7cd3364 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/java/com/javadevjournal/utils/UploadFileProperties.java @@ -0,0 +1,21 @@ +package com.javadevjournal.utils; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "file") +@EnableConfigurationProperties +public class UploadFileProperties { + + private String uploadDir; + + public String getUploadDir() { + return uploadDir; + } + + public void setUploadDir(String uploadDir) { + this.uploadDir = uploadDir; + } +} diff --git a/Spring-Boot/spring-boot-file-upload/src/main/resources/templates/uploadFile.html b/Spring-Boot/spring-boot-file-upload/src/main/resources/templates/uploadFile.html new file mode 100644 index 0000000..d7283d9 --- /dev/null +++ b/Spring-Boot/spring-boot-file-upload/src/main/resources/templates/uploadFile.html @@ -0,0 +1,24 @@ + + +
+

Uploaded File Details

+ + +
+
+ + +
File NameFile Type:URL :
+
+
+

Uploaded New File

+
+ + + + +
File to upload 1:
+
+
+ + \ No newline at end of file