file upload Spring Boot

This commit is contained in:
Ayoosh Sharma
2020-02-23 14:24:55 -08:00
parent 7c2cded63f
commit 1c9c30a890
9 changed files with 414 additions and 0 deletions

View File

@@ -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<Resource> 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());
}
}

View File

@@ -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();
}
}

View File

@@ -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<Resource> 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());
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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<Path> 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;
}
}

View File

@@ -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<Path> getAllFiles();
FileMetaData getFile(String fileName) throws FileNotFoundException;
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,24 @@
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:if="${uploadedFile}">
<h2> Uploaded File Details </h2>
<table>
<tr>
<td th:text="${uploadedFile.fileName}">File Name</td><br>
<td th:text="${uploadedFile.mime}">File Type:</td><br>
<td th:text="${uploadedFile.url}">URL :</td>
</tr>
</table>
</div>
<div>
<h2> Uploaded New File </h2>
<form method="POST" enctype="multipart/form-data" action="/upload-file">
<table>
<tr><td>File to upload 1:</td><td><input type="file" name="file" /></td></tr>
<tr><td></td><td><input type="submit" value="Upload" /></td></tr>
</table>
</form>
</div>
</body>
</html>