Spring

[SpringBoot] 파일 업로드 및 다운로드

꾸준함. 2021. 8. 14. 21:27

개요

파일 업로드와 다운로드는 웹 애플리케이션에서 자주 쓰이는 기능입니다.

최근에 엑셀 다운로드 기능을 개발하면서 파일 다운로드 기능은 구현(https://jaimemin.tistory.com/1889)을 해봤는데 아직 파일 업로드 기능은 실무 프로젝트에 적용해본 적이 없으므로 이번 기회에 파일 업로드에 대해 정리해보겠습니다.

 

1. HTML 폼 전송 방식

HTML 폼 전송 방식에는 아래와 같이 크게 두 방식이 있습니다.

  • application/x-www-form-urlencoded
  • multipart/form-data

 

1.1 application/x-www-form-urlencoded

기존 게시글(https://jaimemin.tistory.com/1805) HTML Form - POST 방식을 참고하시면 간단하게 정리한 내용이 있습니다.

  • Form 태그에 별도의 enctype 옵션이 없을 경우 웹 브라우저는 HTTP 요청 메시지의 헤더에 Content-Type: application/x-www-form-urlencoded를 추가
  • 폼에 입력한 내용을 HTTP Body에 쿼리 파라미터 형식으로 넣어줌
    • ex) user=jaimemin&age=28
  • 정리를 하자면, application/x-www-form-urlencoded 방식은 HTTP Body 내 문자를 넣어 전송

 

1.2 multipart/form-data

  • 파일을 업로드할 때는 HTTP Body 내 문자열이 아닌 바이너리 데이터를 전송해야 하고 이때 사용되는 방식이 multipart/form-data
  • multipart/form-data 방식을 사용하기 위해서는 enctype="multipart/form-data"를 지정해야 함
  • 또한, 보통 파일을 업로드할 때 바이너리 데이터인 파일뿐만 아니라 문자 형식인 데이터도 전송하는 경우가 많음
    • ex) 페이스북에 사진을 업로드할 때 사진(바이너리 데이터) 뿐만 아니라 사진에 대한 간단한 설명 혹은 장문의 감성 글(문자)을 올림
    • multipart인 이유는 단어 그대로 여러 파일과 폼의 내용을 함께 전송할 수 있도록 지원해주기 때문
  • 폼의 입력 결과로 생성된 HTTP 메시지를 살펴보면 각각의 전송 항목이 Content-Disposition이라는 항목별 헤더로 인해 구분되어 있음
    • 폼의 일반 데이터는 각 항목별로 문자가 전송
    • 파일의 경우 파일 이름과 함께 Content-Type이 추가되고 바이너리 데이터로 전송
    • 아래 사진 참고

 

 

2. 서블릿을 통한 파일 업로드

 

2.1 HttpServletRequest 메서드

  • 문자 데이터 같은 경우 기존과 동일하게 getParameter 메서드를 통해 받으면 되고
  • multipart의 경우 여러 데이터가 넘어올 수 있으므로 getParts 메서드를 통해 Collection<Part> 형태의 데이터를 받으면 됨

 

샘플 form


 

* 여러 파일을 동시에 올릴 수 있도록 하기 위해서는 input에 multiple="multiple" 속성을 명시해줘야 함

 

샘플 컨트롤러


 

Multipart 파일 용량 제한 옵션

  • 파일 업로드할 때 사이즈 제한이 없을 경우 OOM 에러가 발생하면서 서버가 다운될 수 있습니다.
  • 따라서, 아래와 같은 옵션을 application.properties 혹은 applicaiton.yml에 작성해줘야 합니다.
    • spring.servlet.multipart.max-file-size=10MB
    • spring.servlet.multipart.max-request-size=100MB
  • spring.servlet.multipart.max-file-size 옵션은 파일 하나의 최대 사이즈를 정의
  • spring.servlet.multipart.max-request-size 옵션은 요청한 전체 파일의 크기 합을 정의

 

2.2 spring.servlet.multipart.enabled 옵션

  • 디폴트 값은 true이며 설정 파일 내 false로 설정할 경우 서블릿 컨테이너가 멀티파트와 관련된 처리를 하지 않아 파일 업로드 처리를 정상적으로 할 수 없음
  • 해당 옵션이 켜져 있는 상태에서 multipart 요청이 들어오면 HttpServletRequest 객체가 RequestFacade 객체에서 StandardMultipartHttpServletRequest 객체로 변하는 것을 확인할 수 있음
    • StandardMultipartHttpServletRequest 객체는 스프링에서 기본적으로 제공하는 MultipartResolver
    • 해당 객체를 사용하면 멀티파트와 관련된 여러 가지 처리를 더 편하게 할 수 있기 때문에 컨트롤러에서 HttpServletRequest가 아닌 MultipartHttpServletRequest를 주입받아 처리하기도 함
    • 하지만, 스프링에서 제공하는 MultipartFile 객체로 처리하는 것이 더 편하므로 자세히 알 필요는 없음

 

2.3 업로드된 파일을 저장하는 코드

  • 서블릿을 통해 업로드 된 파일을 저장하려면 많은 파싱 과정을 거쳐야 함
  • 또한 파일이 저장될 경로를 설정해줘야 하는데 이는 설정 파일에 지정해주면 됨
    • ex) file.download.directory=C:/Users/jaimemin/
    • 마지막에 반드시 슬래시(/)가 포함되어야 함
    • 샘플 코드의 경우 로컬 컴퓨터 내 저장했지만 실제 상용 서비스에서는 별도의 서버에 파일을 저장하고 DB 메모리 과부하를 방지하기 위해 DB에는 해당 파일을 특정할 수 있는 파일명만 저장

 

Servlet에서 제공하는 Part의 주요 메서드

  • getSubmittedFileName(): 클라이언트가 전달한 파일명을 조회하는 메서드
  • getInputStream(): Part의 전송 데이터를 읽는 메서드
  • write(...): Part를 통해 전송된 데이터를 저장하는 메서드


 

3. 스프링 프레임워크를 활용한 파일 업로드

  • 앞서 언급했던 것처럼 스프링은 MultipartFile 인터페이스를 제공하여 멀티파트 파일을 매우 편리하게 다룰 수 있도록 지원
  • @RequestParam 혹은 @ModelAttribute 어노테이션을 통해 MutlipartFile을 불러오면 되며 주요 메서드들은 아래와 같음
    • getOriginalFilename(): 업로드된 파일명을 조회하는 메서드
    • transferTo(...): 파일을 저장하는 메서드


 

* 기존 서블릿을 통한 파일 업로드 코드에 비해 훨씬 간단해진 것을 확인할 수 있습니다.

 

4. 스프링 프레임워크를 활용한 파일 다운로드

  • 저장한 파일 경로를 조회한 뒤 Resource 객체를 ResponseEntity로 감싸서 사용자에게 뿌려주는 기능
  • 이때, HTTP Content-Disposition 헤더에 attachment; filename="업로드된 파일명" 값을 부여해야 함
    • 부여하지 않을 경우 파일이 다운로드되지 않고 웹 브라우저 상에 파일 내용이 조회됨

 

샘플 div container


 

샘플 컨트롤러


 

비고

  • 앞서 언급했던 것처럼 상용 서비스에서는 파일을 별도 서버에 저장하고 DB에는 해당 파일을 특정 지을 수 있는 파일명만 저장
    • 이때, 여러 사용자가 동일한 파일명으로 저장할 수 있으므로 사용자가 업로드한 파일명을 DB에 저장하지 않고 UUID와 같은 고유 id와 확장자를 조합한 문자열로 저장
    • 사용자가 업로드한 파일을 다시 다운로드할 때는 업로드한 파일명으로 다운로드할 수 있도록 DB에 저장할 때 고유 문자열과 업로드한 파일명을 함께 저장하는 것이 일반적인 방법
  • 파일 다운로드를 여러 방법으로 구현할 수 있음
    • 예제에서는 ResonseEntity 객체를 활용했지만
    • ServletOutputStream 객체를 통한 다운로드 기능 구현도 가능

 

출처

인프런 스프링 MVC 2편 (김영한 강사님)

반응형