[ Spring ] 브라우저에서 보낸 파일 받는 과정
[ 문제 ]
현재 회사에서 그리드 row에 추가된 파일들을 서버에 전송하고 저장하는 메뉴를 개발하고 있다.
row에 있는 일반 데이터들과 파일들을 서버에 보낼 때, Spring에서 어떻게 파일들을 받을 수 있는지 그 과정을 정리한 글이다.
[ 단계 나누기 ]
전체 흐름을 쉽게 파악하기 위해 세 가지 계층으로 나누었다.
- 브라우저
- Tomcat
- Spring
# 브라우저
브라우저에서 서버로 row의 데이터들과 파일을 보낼 때, FormData(자바스크립트 내장 객체)를 사용한다.
FormData 만든 후
- 일반 데이터들은 JSON으로 변환 후 FormData 객체에 등록
- 파일의 경우 각 row의 고유값을 합친 파일명을 키로 , file을 값으로 FormData에 등록
현재 회사에서는 jQuery를 사용하고 있으므로 ajax를 통해서 FormData를 서버에 보낸다. 따라서 ajax사용하여 FormData를 보내는데 이 때 HTTP 요청이 생성된다. 브라우저는 boundary를 추가하여 이 때 HTTP 요청 본문은 Byte Stream이다.
# Tomcat
Tomcat은 Byte Stream을 읽어서 Request Line, Header ,Body 로 분리한다.
그리고 HttpServletRequest를 생성한다.
# Spring
Spring의 dispatcherServlet 내부에서는 Tomcat에서 받은 HttpServletRequest를 활용해 checkMultipart가 실행된다.
이 때 Content-Type을 확인하여 multipart/form-data이면 resolveMultipart() 가 실행된다.
resolveMultipart가 실행되면서 HttpServletRequest의 getParts()를 호출한다.
이때 실제 getParts() 구현은 Tomcat의 HttpServletRequest 구현체에서 실행되며, Tomcat은 HTTP Body를 boundary 기준으로 파싱하여 Part 객체를 생성한다.
이후 Spring은 생성된 Part 정보를 이용해 MultipartHttpServletRequest를 생성하고 Controller에 전달한다.
결론적으로 Controller 작성 시, 인자 타입을 MultipartHttpServletRequest로 지정하면 Spring이 생성한 해당 객체를 주입받게 되고, 이를 통해 multipart 요청에 포함된 파일들을 MultipartFile 형태로 가져와 사용할 수 있다.
예시 코드)
- HTML
<input type="file" id="singleFile">
<input type="file" id="multiFile" multiple>
<button id="uploadBtn">업로드</button>
- jQuery Ajax
$("#uploadBtn").click(function () {
const formData = new FormData();
// 단일 파일
const singleFile = $("#singleFile")[0].files[0];
if (singleFile) {
formData.append("file", singleFile);
}
// 여러 파일
const files = $("#multiFile")[0].files;
for (let i = 0; i < files.length; i++) {
formData.append("files", files[i]);
}
$.ajax({
url: "/upload",
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function (response) {
console.log("업로드 성공");
},
error: function (xhr) {
console.log("업로드 실패");
}
});
});
- Spring
@PostMapping("/upload")
public void upload(MultipartHttpServletRequest request) {
MultipartFile file = request.getFile("file");
List<MultipartFile> files = request.getFiles("files");
}