1. HTML 폼 전송방식
스프링 방식의 파일 업로드를 진행하기 전에 servlet을 이용한 파일 업로드를 먼저 진행하고자 한다. 일반적으로
HTML Form을 이용해 데이터를 전송할 경우, Content-Type은 application/x-www-form-urlencoded 으로 알고 있다.
그러면 Body에 쿼리 스트링 방식으로 &로 구분해서 전송이 된다.
문자열만 전송한다고 하면 쿼리스트링 방식으로 전송이 가능하나 만약 업로드 파일을 전송한다면 어떻게 해야 할까?
이럴 경우에는 바이너리 코드를 사용해야 한다. 바이너리 코드란 보통 텍스트를 나타내기 위한 아스키코드의 반대라고 볼 수 있는데, Binary(바이너리)는 2진법을 의미하며, '1'과 '0'만을 사용하여 수를 나타내는 진법이다. 그리고, 우리가 사용하는 모든 컴퓨터는 데이터를 2진법을 사용하여 저장하고, 사용하기에 컴퓨터에게 있어서 바이너리란 사실상 가장 근본이 되는 체계라고 볼 수 있다.
예를 들어 그림 및 파일, 워드 등 텍스트 파일이 아닌 파일은 바이너리 코드로 구성되고 폼을 전송할 때 파일과 문자를 전송하는 경우가 많으므로 결국 파일은 업로드한다는 것은 문자와 바이너리를 동시에 전송해야 함을 의미한다.
이러한 문제를 해결하기 위해서 HTTP는 multipart/form-data 라는 전송 방식을 제공한다.
2. 업로드 사이즈 제한
업로드에 관련한 사이즈를 제한할 때는 application.properties에 하나의 최대 사이즈와 전체 합의 사이즈를 설정이 가능하다.
< application.properties >
//파일 하나의 최대 사이즈, 기본 1MB
spring.servlet.multipart.max-file-size=1MB
//멀티파트 요청 하나에 여러 파일을 업로드 할 수 있는데, 그 전체 합이다. 기본 10MB
spring.servlet.multipart.max-request-size=10MB
3. 서블릿과 파일 업로드
< uploadForm >
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
</head>
<body>
<div class="container">
<div class="py-5 text-center">
</div>
<h4 class="mb-3">업로드</h4>
<form th:action method="post" enctype="multipart/form-data">
<ul>
<li>이름 <input type="text" name="name"></li>
<li>파일<input type="file" name="file"></li>
</ul>
<input type="submit"/>
</form>
</div> <!-- /container -->
</body>
</html>
파일을 전송하려면 enctype이 multipart/form-data가 되어야 한다
multipart/form-data은 post방식으로만 전송이 가능하다.
< application.properties >
//파일 업로드 경로 추가
file.dir=C:/Users/GON/study/file/
원하는 경로에 폴더를 미리 만들고 진행한다.
< UploadController >
@PostMapping("upload")
public String upload(HttpServletRequest request) throws ServletException, IOException {
String name = request.getParameter("name");
log.info("name = {}",name);
// 전송 방식에서 각각 나누어진 부분 확인 가능
Collection<Part> parts = request.getParts();
log.info("parts = {}", parts);
for (Part part : parts) {
log.info("======= PART ===========");
log.info("part.getName={}",part.getName());
Collection<String> headerNames = part.getHeaderNames();
for (String headerName : headerNames) {
log.info("header {} : {}",headerName, part.getHeader(headerName));
}
//편의 메서드
log.info("submittedFileName={}", part.getSubmittedFileName());
log.info("part.getSize={}", 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 "uploadForm";
}
requset.getParameter()로는 multipart/form-data형식으로 보낸 값은 받을 수 없다. 파일을 part를 이용해서 받아와야 한다. 멀티파트 형식은 전송 데이터를 하나하나 각각 부분( Part )으로 나누어 전송하는데 parts 에는 이렇게 나누어진 데이터가 각각 담긴다. 서블릿이 제공하는 Part 는 멀티파트 형식을 편리하게 읽을 수 있는 다양한 메서드를 제공한다.
- part.getSubmittedFileName() : 클라이언트가 전달한 파일명
- part.getInputStream() : Part의 전송 데이터를 읽을 수 있다.
- part.write( 경로 ) : 해당 경로에 전송된 데이터를 저장
단일 파일 업로드를 할 수 있지만 다중 파일 업로드를 진행하고자 하기 때문에 getPart()가 아닌 getParts()를 사용했다.
part.getSubmittedFileName()을 통해 클라이언트가 전달한 파일명을 fileDir (application.properties에서 설정한 경로)
붙여 part.write(fullPath)를 통해 설정된 경로에 데이터를 저장한다.
< 출력내용>
<console>
======= PART ===========
part.getName=itemName
header content-disposition : form-data; name="itemName"
submittedFileName=null
size=7
body=파일2
======= PART ===========
part.getName=file
header content-disposition : form-data; name="file"; filename="coffee01.png"
header content-type : image/png
submittedFileName=coffee01.png
size=216980
body=�PNG
설정한 폴더에 잘 저장된 것을 알 수 있다. 만약 저장되지 않았다면 파일 저장 경로를 다시 확인하면 된다.
'SPRING' 카테고리의 다른 글
2. 파일 업로드 (Spring) (0) | 2021.08.19 |
---|---|
Converter (0) | 2021.08.09 |
예외처리 - HandlerExceptionResolver (0) | 2021.08.07 |
Filter와 Interceptor ( 2. Interceptor ) - 로그인 관련 (0) | 2021.08.06 |
Filter와 Interceptor ( 1. Filter ) - 로그인 관련 (0) | 2021.08.05 |