ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring MVC - Servlet의 파일 업로드
    Spring/Spring MVC 2022. 2. 17. 21:34
    728x90
    반응형

     

     

     

     

    Servlet의  파일 업로드

     HttpServletRequest를 이용하여 HTTP Form 멀티파트 전송 방식으로 넘어온 데이터를 어떻게 이용하는지 살펴보자.

     

     

     

     

     

     

     

    HTML Form 전송 방식

     

    • Form의 Content-Type은 다음과 같이 2가지 방식이 있다.
    • application/x-www-form-urlencoded 
           - 일반적인 form 전송 방식
           - form 태그 내의 입력된 일반 데이터들을 쿼리 파라미터 형식으로 메시지 바디에 담아 전송
    • multipart/form-data
           - 첨부파일을 포함한 전송 방식
           - form 태그 내의 입력된 일반 데이터 문자들과 바이너리(첨부파일)를 함께 전송
           - 메시지 바디에는 Content-Disposition이라는 항목별 헤더가 추가된다
           - 메시지 바디에 각 항목별로 Part로 나누어져 전송

     


     

    multipart/form-data 전송 방식의 HTTP 요청 메시지

     

     

    multipart/form-data 방식으로 요청한 HTTP 요청 메시지 로그 확인

     

     HTTP Form 전송 방식 중 하나인 multipart/form-data 방식으로 전송될 때 어떻게 HTTP 요청 메시지가 어떻게 전달되는지 간단한 html을 작성하여 로그를 확인해보자.

     

    <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>
    @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";
        }
    }

     

     위에서 작성한 form을 입력하여 요청하면 다음과 같은 HTTP 요청 메시지 로그를 확인할 수 있다

     

     위처럼 form의 입력된 데이터만큼 Part로 나누어져 요청 메시지 바디에 전달되는 것을 볼 수 있다. Servlet은 Part로 나누어진 데이터들을 사용할 수 있도록 가공 처리해주기 때문에 request.getParameter, request.getParts을 이용하여 데이터들을 사용할 수 있다.

     

     


     

    multipart/form-data 옵션

     

    • application.properties에 해당 옵션을 설정할 수 있다.
    • spring.servlet.multipart.max-file-size 
             - 파일 하나의 최대 사이즈 설정
             - 기본 1MB
    • spring.servlet.multipart.max-request-size
             - 여러 파일을 업로드할 때 합친 최대 사이즈 설정
             - 기본 10MB
    • spirng.servlet.multipart.enabled
             - 스프링 부트가 멀티파트 데이터를 처리하도록 설정
             - 기본 true
             - false로 설정하면 멀티파트 데이터를 처리하지 못한다.

     

    Servlet을 이용한 파일 업로드

     위에서 멀티파트 데이터가 어떻게 넘어오는지 로그를 통해 확인해 보았다. 이제 실제 파일을 서버에 업로드하는 방법을 알아보자.

     

     

    파일이 저장되는 경로 설정

     

    • apllication.properties에 파일이 저장되는 경로를 설정
    • file.dir=파일 업로드 경로
      ex) file.dir=C:/tools/spring/Spring_Lab/file/​
    • 경로 마지막에 슬래시(/)가 포함되어야 한다

     

    file.dir=C:/tools/spring/Spring_Lab/file/

     


     

    지정된 경로에 파일 저장

     

    • 서블릿이 제공하는 Part는 멀티파트 형식의 데이터를 편리하게 읽을 수 있는
      다양한 메서드를 제공한다. (각 part의 헤더와 바디를 읽어올 수 있다)
    • 제공 메서드
            - part.getSubmittedFileName() : 클라이언트가 전달한 파일명
            - part.getInputStream() : Part의 전송 데이터를 읽을 수 있다.
            - part.write(...) : Part를 통해 전송된 데이터를 저장할 수 있다.

     

        @Value("${file.dir}")
        private String fileDir;
        
        ...
    
    
        @PostMapping("/upload")
        ublic 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("headr {} : {}", headerName, part.getHeader(headerName));
                }
                //편의 메서드
                log.info("submittedFiledName={}", part.getSubmittedFileName());
                log.info("size={}", part.getSize());
    
                //데이터 읽기
                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로 나누어진 데이터들은

    getParts메서드를 각각의 Part에 담긴 헤더 정보와 바디 정보를 읽어 올 수 있다. 또한 Part의 바디에 있는 데이터들은 옛날에는 part.getInputStream() 를 통해 읽어와야 했지만 요즘에는 서블릿이 내부적으로 처리해줘서 getParameter로 읽어올 수 있다.

     

    form에서 전송한 파일이 저장된 것을 볼 수 있다.

     

     

    728x90
    반응형

    댓글

Designed by Tistory.