[study] 스프링MVC2 - 10. 파일 업로드
김영한님의 스프링 mvc2편 - 벡엔트 웹 개발 활용 기술 을 듣고 정리한 내용입니다.
HTML 폼 전송 방식
- application/x-www-form-urlencoded
- multipart/form-data
Content-Type: application/x-www-form-urlencoded 방식으로 전송할 경우
문자열과 함께 첨부파일을 함께 전송해야 되는 경우 문제가 있다.
따라서 문자와 바이너리를 동시에 전송할 땐 multipart/form-data 방식을 사용한다
폼의 입력 결과로 생성된 HTTP 메시지를 보면 각각의 전송 항목이 구분이 되어있다.
Content-Disposition 이라는 항목별 헤더가 추가되어 있고 부가정보가 존재한다.
서블릿과 파일 업로드
@Slf4j
@Controller
@RequestMapping("/servlet/v1")
public class ServletUploadControllerV1 {
@GetMapping("/upload")
public String newFile() {
return "upload-form";
}
@PostMapping("/upload")
public String saveFileV1(HttpServletRequest request) throws ServletException, IOException {
log.info("request={}", request);
String itemName = request.getParameter("itemName");
log.info("itemName={}", itemName);
Collection<Part> parts = request.getParts();
log.info("parts={}", parts);
return "upload-form";
}
}
request.getParts() : multipart/form-data 전송 방식에서 각각 나누어진 부분을 받아서 확인할 수 있다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>상품 등록 폼</h2>
</div>
<h4 class="mb-3">상품 입력</h4>
<form th:action method="post" enctype="multipart/form-data">
<ul>
<li>상품명 <input type="text" name="itemName"></li>
<li>파일<input type="file" name="file" ></li>
</ul>
<input type="submit"/>
</form>
</div> <!-- /container -->
</body>
</html>
결과 로그
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryIkmRCsDjBoqIZhWc
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Whale/3.21.192.18 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/servlet/v1/upload
Accept-Encoding: gzip, deflate, br
Accept-Language: ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7,ja;q=0.6,de;q=0.5
------WebKitFormBoundaryIkmRCsDjBoqIZhWc
Content-Disposition: form-data; name="itemName"
test
------WebKitFormBoundaryIkmRCsDjBoqIZhWc
Content-Disposition: form-data; name="file"; filename="KakaoTalk_20230805_173811541.jpg"
Content-Type: image/jpeg
멀티파트 사용 옵션
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.enabled=true (기본 true)
spring.servlet.multipart.enabled 옵션을 켜면
스프링의 DispatcherServlet 에서 멀티파트 리졸버( MultipartResolver )를 실행한다.
@Slf4j
@Controller
@RequestMapping("/servlet/v2")
public class ServletUploadControllerV2 {
@Value("${file.dir}")
private String fileDir;
@GetMapping("/upload")
public String newFile() {
return "upload-form";
}
@PostMapping("/upload")
public String saveFileV1(HttpServletRequest request) throws ServletException, IOException {
log.info("request={}", request);
String itemName = request.getParameter("itemName");
log.info("itemName={}", itemName);
Collection<Part> parts = request.getParts();
log.info("parts={}", parts);
for (Part part : parts) {
log.info("==== PART ====");
log.info("name={}", part.getName());
Collection<String> headerNames = part.getHeaderNames();
for (String headerName : headerNames) {
log.info("header {}: {}", headerName, part.getHeader(headerName));
}
//편의 메서드
//content-disposition; filename
log.info("submittedFilename={}", part.getSubmittedFileName());
log.info("size={}", part.getSize()); //part body size
//데이터 읽기
InputStream inputStream = part.getInputStream();
String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("body={}", body);
//파일에 저장하기
if (StringUtils.hasText(part.getSubmittedFileName())) {
String fullPath = fileDir + part.getSubmittedFileName();
log.info("파일 저장 fullPath={}", fullPath);
part.write(fullPath);
}
}
return "upload-form";
}
}
Part 주요 메서드
part.getSubmittedFileName() : 클라이언트가 전달한 파일명
part.getInputStream(): Part의 전송 데이터를 읽을 수 있다.
part.write(...): Part를 통해 전송된 데이터를 저장할 수 있다.
결과로그
2023-08-15 21:04:31.263 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : ==== PART ====
2023-08-15 21:04:31.263 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : name=itemName
2023-08-15 21:04:31.263 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : header content-disposition: form-data; name="itemName"
2023-08-15 21:04:31.263 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : submittedFilename=null
2023-08-15 21:04:31.263 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : size=4
2023-08-15 21:04:31.264 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : body=test
2023-08-15 21:04:31.264 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : ==== PART ====
2023-08-15 21:04:31.264 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : name=file
2023-08-15 21:04:31.264 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : header content-disposition: form-data; name="file"; filename="KakaoTalk_20230805_173811541.jpg"
2023-08-15 21:04:31.264 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : header content-type: image/jpeg
2023-08-15 21:04:31.264 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : submittedFilename=KakaoTalk_20230805_173811541.jpg
2023-08-15 21:04:31.264 INFO 292 --- [nio-8080-exec-2] h.u.c.ServletUploadControllerV2 : size=318310
Y �
[ XYZ �� �-mluc enUS G o o g l e I n c . 2 0 1 6�� C
스프링은 MultipartFile 이라는 인터페이스로 멀티파트 파일을 매우 편리하게 지원한다.
@Slf4j
@Controller
@RequestMapping("/spring")
public class SpringUploadController {
@Value("${file.dir}")
private String fileDir;
@GetMapping("/upload")
public String newFile() {
return "upload-form";
}
@PostMapping("/upload")
public String saveFile(@RequestParam String itemName,
@RequestParam MultipartFile file, HttpServletRequest request) throws IOException {
log.info("request={}", request);
log.info("itemName={}", itemName);
log.info("multipartFile={}", file);
if (!file.isEmpty()) {
String fullPath = fileDir + file.getOriginalFilename();
log.info("파일 저장 fullPath={}", fullPath);
file.transferTo(new File(fullPath));
}
return "upload-form";
}
}
@RequestParam MultipartFile file 업로드하는 HTML Form의 name에 맞추어 @RequestParam 을 적용하면 된다.
추가로 @ModelAttribute 에서도 MultipartFile 을 동일하게 사용할 수 있다.
MultipartFile 주요 메서드
- file.getOriginalFilename() : 업로드 파일 명
- file.transferTo(...) : 파일 저장
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 인프런 | 강의
웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있
www.inflearn.com