개발자의 오르막
Spring Boot REST 기반 파일업로드 본문
# Spring Boot 기본 환경설정
- Spring Boot 2.1.8 / Maven / war / JPA / Mysql /
일단 기본 설정으로 Tymeleaf 와 Spring Web, DevTools 를 체크해준다.
그리고 파일 업로드를 위해 application.properties 에 설정을 추가해준다.
여기서 file.upload-dir 이 property에 등록이 안되어 있다.. !! (이부분이 중요)
추후 등록해주겠다.
그럼 이제 제일 중요한 property 등록을 먼저 하겠다.
우선 패키지 파일트리를 그림과 같이 구성한다.
그리고 property 부터 등록을 하겠다.
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadDir;
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
}
이 때 중요한 부분이 이부분
이 부분에서 Add spring-boot-configuration-processor to pom.xml 을 클릭해서
등록을 해줘야
이렇게 등록이 된다.. 이게 안떠서 거의 4시간을 찾아 보기만 하다가,
노가다로 알아냈다... property 등록.. 일단 이 알람이 떠야 한다!!
그리고 Spring boot Application 에 Bean 으로 등록을 해줘야 한다.
# Spring Boot Application
@SpringBootApplication
@EnableConfigurationProperties({
FileStorageProperties.class
})
public class CatchreviewVo4Application {
public static void main(String[] args) {
SpringApplication.run(CatchreviewVo4Application.class, args);
}
}
# exception 패키지의 FileStorageException
public class FileStorageException extends RuntimeException {
public FileStorageException(String message) {
super(message);
}
public FileStorageException(String message, Throwable cause) {
super(message, cause);
}
}
# exception 패키지의 MyFileNotFoundException
@ResponseStatus(HttpStatus.NOT_FOUND)
public class MyFileNotFoundException extends RuntimeException {
public MyFileNotFoundException(String message) {
super(message);
}
public MyFileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
# service 패키지의 FileStorageService
@Service
public class FileStorageService {
private final Path fileStorageLocation;
@Autowired
public FileStorageService(FileStorageProperties fileStorageProperties) {
this.fileStorageLocation = Paths.get(fileStorageProperties.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(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 MyFileNotFoundException("File not found " + fileName);
}
} catch (MalformedURLException ex) {
throw new MyFileNotFoundException("File not found " + fileName, ex);
}
}
}
# domain 패키지의 UploadFileResponse
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;
}
}
# controller 패키지의 FileController
@Log
@RestController
public class FileController {
private static final Logger logger = LoggerFactory.getLogger(FileController.class);
@Autowired
private FileStorageService fileStorageService;
@PostMapping("/uploadFile")
public UploadFileResponse uploadFile(@RequestParam("file") MultipartFile file) {
System.out.println("---------------uploadFile");
log.info("uploadFile");
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 List<UploadFileResponse> uploadMultipleFiles(
@RequestParam("files") MultipartFile[] files) {
return Arrays.asList(files)
.stream()
.map(file -> uploadFile(file))
.collect(Collectors.toList());
}
@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);
}
@RequestMapping("/uploadFile")
public void forGues() {
log.info("file==========================");
}
}
이후에 스프링 부트 어플리케이션을 실행시켜서 제대로 부팅 되는지 확인해보자!
# html 부분
# script
'use strict';
var singleUploadForm = document.querySelector('#singleUploadForm');
var singleFileUploadInput = document.querySelector('#singleFileUploadInput');
var singleFileUploadError = document.querySelector('#singleFileUploadError');
var singleFileUploadSuccess = document.querySelector('#singleFileUploadSuccess');
var multipleUploadForm = document.querySelector('#multipleUploadForm');
var multipleFileUploadInput = document.querySelector('#multipleFileUploadInput');
var multipleFileUploadError = document.querySelector('#multipleFileUploadError');
var multipleFileUploadSuccess = document.querySelector('#multipleFileUploadSuccess');
function uploadSingleFile(file) {
var formData = new FormData();
formData.append("file", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/uploadFile", true);
xhr.onload = function() {
console.log(xhr.responseText);
var response = JSON.parse(xhr.responseText);
alert(xhr.responseText);
if(xhr.status == 200) {
alert('성공?');
singleFileUploadError.style.display = "none";
singleFileUploadSuccess.innerHTML = "<p>File Uploaded Successfully.</p><p>DownloadUrl : <a href='" + response.fileDownloadUri + "' target='_blank'>" + response.fileDownloadUri + "</a></p>";
singleFileUploadSuccess.style.display = "block";
} else {
alert('실패?');
singleFileUploadSuccess.style.display = "none";
singleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred";
}
}
xhr.send(formData);
}
function uploadMultipleFiles(files) {
var formData = new FormData();
for(var index = 0; index < files.length; index++) {
formData.append("files", files[index]);
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "/uploadMultipleFiles");
xhr.onload = function() {
console.log(xhr.responseText);
var response = JSON.parse(xhr.responseText);
if(xhr.status == 200) {
multipleFileUploadError.style.display = "none";
var content = "<p>All Files Uploaded Successfully</p>";
for(var i = 0; i < response.length; i++) {
content += "<p>DownloadUrl : <a href='" + response[i].fileDownloadUri + "' target='_blank'>" + response[i].fileDownloadUri + "</a></p>";
}
multipleFileUploadSuccess.innerHTML = content;
multipleFileUploadSuccess.style.display = "block";
} else {
multipleFileUploadSuccess.style.display = "none";
multipleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred";
}
}
xhr.send(formData);
}
singleUploadForm.addEventListener('submit', function(event){
var files = singleFileUploadInput.files;
alert(files.length);
if(files.length === 0) {
singleFileUploadError.innerHTML = "Please select a file";
singleFileUploadError.style.display = "block";
}
uploadSingleFile(files[0]);
event.preventDefault();
}, true);
multipleUploadForm.addEventListener('submit', function(event){
var files = multipleFileUploadInput.files;
if(files.length === 0) {
multipleFileUploadError.innerHTML = "Please select at least one file";
multipleFileUploadError.style.display = "block";
}
uploadMultipleFiles(files);
event.preventDefault();
}, true);
되네.....
'SpringFrameWork > SpringBoot' 카테고리의 다른 글
[Spring Boot + JPA ] DomainClassConvert, Pageable (0) | 2019.10.08 |
---|---|
Spring Boot + REST API 개념부터 설계까지 (0) | 2019.10.08 |
Spring Boot 기반 REST API 설계 (화면에서 DB까지) (0) | 2019.09.27 |
Springboot - jpa 연동 환경 설정 (0) | 2019.09.06 |
Springboot 개발환경 구축, lombok, junit (1) | 2019.09.05 |