[Spring Boot] 7. File Upload
* 디렉토리 구조 및 공통 클래스
package org.example.fileupload;
@SpringBootApplication
public class Chap06FileUploadApplication {
public static void main(String[] args) {
SpringApplication.run(Chap06FileUploadApplication.class, args);
}
}
package org.example.fileupload;
@Controller
public class MainController {
@GetMapping(value = {"/", "/main"})
public String main() {
return "main";
}
}
Single File Upload
1. application.yml 설정하기
spring-boot-starter-web에는
파일 업로드를 위한 multipartResolver가 기본 빈으로 등록되어 있기 때문에 추가적으로 등록할 필요는 없다.
파일 저장 경로, 용량 등에 대한 설정을 application.yml, 또는 application.properties를 통해 할 수 있다.
파일의 크기가 file-size-threshold 값 이하라면
임시파일을 생성하지 않고 메모리에서 즉시 파일을 읽어서 생성할 수 있어 속도는 빠르지만
스레드가 작업을 수행하는 동안 부담이 될 수 있다.
파일 크기가 file-size-threshold 값을 초과한다면
파일은 spring.servlet.multipart.location 경로에 저장되어 해당 파일을 읽어 작업을 해야한다.
# application.yml
spring:
servlet:
multipart:
# 파일 저장 경로
location: F:/bootcamp/spring/05_spring/02_spring-web/chap06-file-upload
# 최대 업로드 파일 크기
max-file-size: 10MB
# 최대 요청 파일 크기
max-request-size: 10MB
# application.properties
# 파일 저장 경로
# 이 예제에서는 프로젝트 내부 경로에 업로드 파일을 저장할 것이므로 절대 경로를 이용한다.
# spring.servlet.multipart.location=프로젝트절대경로
spring.servlet.multipart.location=C:/Lecture/05_spring/02_spring-web/chap06-file-upload
# 최대 업로드 파일 크기
spring.servlet.multipart.max-file-size=10MB
# 최대 요청 파일 크기
spring.servlet.multipart.max-request-size=10MB
2. 파일 업로드 뷰 페이지 작성하기
업로드 폼을 보여줄 view 페이지를 작성한다.
<!--/main/resources /templates/main.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>
<h1>File Upload Test</h1>
<!--
form 태그의 기본 전송 content type은 application/x-www-form-urlencoded 으로 설정 되어 있으나
file을 전송할 경우에는 encoding type을 multipart/form-data로 변경해야 한다.
-->
<h3>single file upload</h3>
<form action="single-file" method="post" enctype="multipart/form-data">
파일 : <input type="file" name="singleFile"><br>
파일 설명 : <input type="text" name="singleFileDescription"><br>
<input type="submit">
</form>
</body>
</html>
3. 파일 업로드를 처리할 controller 생성하기
Spring Framework에서는 MultipartFile 클래스를 이용하여 파일 업로드를 처리한다.
이를 처리할 controller인 FileUploadController를 생성하자.
@RequestParam 어노테이션을 이용하여
요청 파라미터 중 singleFile이라는 이름으로 전송된 파일을 MultipartFile 객체로 받아온다.
이후 해당 파일을 rename 처리하여 저장하고, result라는 view 페이지를 반환한다.
package org.example.fileupload;
@Controller
public class FileUploadController {
@PostMapping("/single-file")
public String singleFileUpload(
@RequestParam String singleFileDescription,
@RequestParam MultipartFile singleFile,
Model model
) {
System.out.println("singleFileDescription: " + singleFileDescription);
System.out.println("singleFile: " + singleFile);
/* 서버로 전송 된 MultipartFile 객체(파일)를 저장할 경로 설정 */
String root = "src/main/resources/static";
String filePath = root + "/uploadFiles";
File dir = new File(filePath);
if(!dir.exists()) dir.mkdirs();
/* 파일명이 중복 되면 해당 경로에서 덮어쓰기 될 수 있으므로 중복 되지 않는 이름으로 변경 */
String originFileName = singleFile.getOriginalFilename();
String ext = originFileName.substring(originFileName.lastIndexOf("."));
String savedName = UUID.randomUUID() + ext;
try {
/* 파일 저장 */
singleFile.transferTo(new File(filePath + "/" + savedName));
model.addAttribute("message", "파일 업로드 완료");
} catch (IOException e) {
model.addAttribute("message", "파일 업로드 실패");
throw new RuntimeException(e);
}
return "result";
}
}
4. 반환 view 페이지 작성하기
result.html 파일을 작성하자.
<!--/main/resources/templates/result.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>result</title>
</head>
<body>
<h1 th:text="${message}"></h1>
</body>
</html>
MultiFile Upload
1. 파일 업로드 view 페이지 작성하기
업로드 폼을 보여줄 view 페이지를 작성하자.
위에서 작성한 main.html에 코드를 추가해주었다.
<!--/main/resources/templates/main.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>
<!--
form 태그의 기본 전송 content type은 application/x-www-form-urlencoded 으로 설정 되어 있으나
file을 전송할 경우에는 encoding type을 multipart/form-data로 변경해야 한다.
-->
<!--위에서 작성한 내용 생략-->
<h3>multi file upload</h3>
<form action="multi-file" method="post" enctype="multipart/form-data">
파일 : <input type="file" name="multiFiles" multiple><br> <!--multiple은 파일 여러개를 한 번에 업로드하기 위한 속성이다.-->
파일 설명 : <input type="text" name="multiFilesDescription"><br>
<input type="submit">
</form>
</body>
</html>
2. 파일 업로드를 처리할 controller 생성하기
위에서 작성한 FileUploadController에 코드를 추가해주었다.
업로드 파일이 같은 name 속성으로 여러 개 전달되므로 MultipartFile 클래스는 List 타입으로 선언해야 한다.
@RequestParam 어노테이션을 이용하여
요청 파라미터 중 multiFiles라는 이름으로 전송된 파일을 List<MultipartFile> 객체로 받아온다.
이후 해당 파일을 처리하는데 필요한 정보를 DTO 타입을 선언해서 다룬다.
추후 DB에 저장하는 등의 작업을 수행하는데,
실패 시 이전에 저장된 파일은 삭제하고 최종적으로 result라는 view 페이지를 반환한다. (위에서 작)
package org.example.fileupload;
@Controller
public class FileUploadController {
// 위에서 작성한 코드 생략
@PostMapping("/multi-file")
public String multiFileUpload(
@RequestParam String multiFilesDescription,
@RequestParam List<MultipartFile> multiFiles,
Model model
) {
/* 서버로 전송 된 MultipartFile 객체를 서버에서 지정하는 경로에 저장한다. */
String root = "src/main/resources/static";
String filePath = root + "/uploadFiles";
File dir = new File(filePath);
if(!dir.exists()) dir.mkdirs();
List<FileDTO> files = new ArrayList<>();
try {
/* 다중 파일이므로 반복문을 이용한 처리를 하고 저장한다. */
for(MultipartFile multiFile : multiFiles) {
/* 파일명이 중복 되면 해당 경로에서 덮어쓰기 될 수 있으므로 중복 되지 않는 이름으로 변경 */
String originFileName = multiFile.getOriginalFilename();
String ext = originFileName.substring(originFileName.lastIndexOf("."));
String savedName = UUID.randomUUID() + ext;
/* 파일 저장 */
multiFile.transferTo(new File(filePath + "/" + savedName));
/* 파일에 관한 정보를 FileDTO에 담아 List에 보관 */
files.add(new FileDTO(originFileName, savedName, filePath, multiFilesDescription));
}
// files 정보는 DB에 insert 된다.
model.addAttribute("message", "다중 파일 업로드 완료");
} catch (IOException e) {
/* 파일 저장이 중간에 실패할 경우 이전에 저장된 파일 삭제 */
for(FileDTO file : files) new File(filePath + "/" + file.getSavedFileName()).delete();
model.addAttribute("message", "다중 파일 업로드 실패");
// throw new RuntimeException(e);
}
return "result";
}
}
3. FileDTO
package org.example.fileupload;
/* 업로드 된 파일과 관련한 정보를 모아서 관리하는 DTO 클래스 */
public class FileDTO {
private String originFileName;
private String savedFileName;
private String filePath;
private String fileDescription;
public FileDTO(String originFileName, String savedFileName, String filePath, String fileDescription) {
this.originFileName = originFileName;
this.savedFileName = savedFileName;
this.filePath = filePath;
this.fileDescription = fileDescription;
}
public String getOriginFileName() {
return originFileName;
}
public void setOriginFileName(String originFileName) {
this.originFileName = originFileName;
}
public String getSavedFileName() {
return savedFileName;
}
public void setSavedFileName(String savedFileName) {
this.savedFileName = savedFileName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getFileDescription() {
return fileDescription;
}
public void setFileDescription(String fileDescription) {
this.fileDescription = fileDescription;
}
@Override
public String toString() {
return "FileDTO{" +
"originFileName='" + originFileName + '\'' +
", savedFileName='" + savedFileName + '\'' +
", filePath='" + filePath + '\'' +
", fileDescription='" + fileDescription + '\'' +
'}';
}
}
위의 과정을 통해 지정된 경로에 파일이 업로드 되는 것을 확인할 수 있다.