파일 업로드
HTML Form을 통한 파일 업로드에는 다음 두 가지 방식이 있습니다.
• application/x-www-form-urlencoded
• multipart/form-data
application/x-www-form-urlencoded 방식은 HTML 폼 데이터를 서버로 전송하는 가장 기본적인 방법입니다.
Form 태그에 별도의 enctype 옵션이 없으면 웹 브라우저는 요청 HTTP 메시지의 헤더에 다음 내용을 추가합니다.
Content-Type: application/x-www-form-urlencoded
그리고 폼에 입력한 전송할 항목을 HTTP Body에 문자로 username=kim&age=20 와 같이 & 로 구분해서 전송합니다.
파일을 업로드 하려면 파일은 문자가 아니라 바이너리 데이터를 전송해야 합니다.
문자를 전송하는 application/x-www-form-urlencoded 방식으로 파일을 전송하기는 어렵습니다.
그리고 또 한가지 문제가 더 있는데, 보통 폼을 전송할 때 파일만 전송하는 것이 아니라는 점입니다.
즉 [ 제목, 내용, 파일 ] 처럼 문자 + 파일의 데이터를 전송하는 것이 대부분 입니다.
즉 문자와 바이너리를 동시에 전송해야합니다.
하여 HTTP에는 multipart/form-data 방식을 제공합니다.
왼쪽의 html에서 보이듯이 위의 방식을 사용하기 위해서는 form태그에 별도의
enctype="multipart/form-data" 를 지정해줘야 합니다.
multipart/form-data 방식은 다른 종류의 여러 파일과 폼의 내용을 함께 전송할 수 있습니다.
폼의 입력 결과로 생성된 HTTP 메시지를 보면 각각의 전송 항목이 구분이 되어있습니다.
Content- Disposition 이라는 항목별 헤더가 추가되어 있고 여기에 부가 정보가 있습니다.
예제에서는 username , age , file1 이 각각 분리되어 있고, 폼의 일반 데이터는 각 항목별로 문자가 전송되고, 파일의 경우 파일 이름과 Content-Type이 추가되고 바이너리 데이터가 전송됩니다.
multipart/form-data 는 이렇게 각각의 항목을 구분해서, 한번에 전송하는 것입니다.
Part :
multipart/form-data 는 application/x-www-form-urlencoded 와 비교해서 매우 복잡하고 각각의 부분( Part )으로 나누어져 있습니다. 그렇다면 이렇게 복잡한 HTTP 메시지를 서버에서 어떻게 받을 수 있는지 알아보겠습니다.
먼저 아래와 같은 컨트롤러를 만들었습니다.
upload-form에서 문자와 파일을 받을 수 있는 컨트롤러가 있습니다.
upload-form.html은 아래와 같습니다.
enctype="multipart/form-data" 를 별도로 추가한 것을 보실 수 있습니다.
이제 위의 form에서 서버로 문자와 파일을 전송해보겠습니다.
제출을 하면 콘솔에 로그와 여러 정보가 남는데, 다음과 같은 정보를 확인할 수 있습니다.
multipart형식으로 잘 받아진 것 같습니다.
parts에 문자 정보와 파일 정보 두가지가 들어간 것을 볼 수 있습니다.
멀티파트 사용 옵션
1. 크기 제한
먼저 파일의 크기를 아래와 같이 application,properties에서 제한 할 수 있습니다.
사이즈를 넘으면 예외( SizeLimitExceededException )가 발생합니다.
max-file-size : 파일 하나의 최대 사이즈, 기본 1MB
max-request-size : 멀티파트 요청 하나에 여러 파일을 업로드 할 수 있는데, 그 전체 합. 기본 10MB
2. multipart사용 제한
아래와 같이 multipart의 사용을 제한할 수 있습니다.(기본은 true입니다.)
멀티파트는 일반적인 폼 요청인 application/x-www-form-urlencoded 보다 훨씬 복잡합니다.
spring.servlet.multipart.enabled 옵션을 끄면 서블릿 컨테이너는 멀티파트와 관련된 처리를 하지 않게 됩니다.
그래서 결과 로그를 보면 request.getParameter("itemName") , request.getParts() 의 결과가 비어있습니다.
spring.servlet.multipart.enabled 켜기:
spring.servlet.multipart.enabled=true (기본 true)
이 옵션을 켜면 스프링 부트는 서블릿 컨테이너에게 멀티파트 데이터를 처리하라고 설정합니다.
참고
spring.servlet.multipart.enabled 옵션을 켜면 스프링의 DispatcherServlet 에서 멀티파트 리졸버( MultipartResolver )를 실행합니다.
멀티파트 리졸버는 멀티파트 요청인 경우 서블릿 컨테이너가 전달하는 일반적인 HttpServletRequest 를 MultipartHttpServletRequest 로 변환해서 반환합니다.
MultipartHttpServletRequest 는 HttpServletRequest 의 자식 인터페이스이고, 멀티파트와 관련된 추가 기능을 제공합니다.
스프링이 제공하는 기본 멀티파트 리졸버는 MultipartHttpServletRequest 인터페이스를 구현한 StandardMultipartHttpServletRequest 를 반환합니다. 이제 컨트롤러에서 HttpServletRequest 대신에 MultipartHttpServletRequest 를 주입받을 수 있는데, 이것을 사용하면 멀티파트와 관련된 여러가지 처리를 편리하게 할 수 있습니다. 그런데 이후 MultipartFile 이라는 것을 사용하는 것이 더 편하기 때문에 MultipartHttpServletRequest 를 잘 사용하지는 않습니다.
이제 서버에서 받은 파일 정보를 통해 직접 저장하는 법을 알아보겠습니다.
파일 저장하기
먼저 업로드한 파일을 저장하려면 실제 파일이 저장되는 경로가 필요합니다. 해당 경로에 실제 폴더를 만들어두고, application.properties에 다음과 같이 경로를 추가합니다.
이전의 컨트롤러에 더 추가하여, 업로드된 파일에 대한 정보를 로그로 보겠습니다.
Part 주요 메서드 :
part.getSubmittedFileName() : 클라이언트가 전달한 파일명
part.getInputStream() : Part의 전송 데이터를 읽을 수 있다.
part.write(...) : Part를 통해 전송된 데이터를 저장할 수 있다.
파일 전송을 원하는 폴더에 저장하기 위해 다음과 같이 경로를 컨트롤러에 알려줘야 합니다.
이렇게 하면 우리가 application.properties에 명시한 경로를 읽고, fileDir가 그 경로가 됩니다.
이제 파일을 업로드해보겠습니다.
제출을 누르면 아래와 같은 로그가 출력됩니다.
그리고 지정한 폴더에 업로드한 파일이 저장되었습니다.
참고
큰 용량의 파일을 업로드를 테스트 할 때는 로그가 너무 많이 남아서 다음 옵션을 끄는 것이 좋습니다. logging.level.org.apache.coyote.http11=debug
다음 부분도 파일의 바이너리 데이터를 모두 출력하므로 끄는 것이 좋습니다.
log.info("body={}", body);
다음에는 더 편리한 스프링이 제공하는 파일업로드 기능을 알아보겠습니다.
'Spring > spring mvc 심화' 카테고리의 다른 글
스프링 파일 업로드 (0) | 2021.07.20 |
---|---|
포맷터 - Formatter (0) | 2021.07.18 |
뷰 템플릿에 컨버터 적용하기 (0) | 2021.07.18 |
컨버젼 서비스 (0) | 2021.07.18 |
타입 컨버터 (0) | 2021.07.17 |