스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술 : 파일 업로드

jkky98·2024년 8월 13일
0

Spring

목록 보기
31/77

파일 업로드

일반적으로 사용하는 html Form을 통한 파일업로드를 이해하기 위해서는 다음 두 가지의 http 미디어 타입의 차이를 이해해야 한다.

  • application/x-www-form-urlencoded : 폼 데이터를 서버로 전송하기 위한 가장 기본적인 방법 (Content-Type)
  • multipart/form-data: 파일과 문자 데이터를 동시에 전송해야하는 상황 (form 태그에 별도의 enctype="multipart/form-data" 지정해야함.)

서블릿 파일 업로드

html에서 폼을 통해 multipart/form-data로 데이터가 넘어온다면 서블릿 의 HttpServletRequest는 request.getParts()로 하여금 Part를 구분하여 데이터를 받아볼 수 있다.

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

멀티파트 사용 옵션(스프링 기능)

어플리케이션 설정에서 우리는 넘어올 파일의 용량을 제한할 수 있다. 용량을 넘어 파일 데이터가 서버 내로 들어올 경우 SizeLimitExceededException 예외가 발생한다.

spring.servlet.multipart.max-file-size=1MB // 파일 하나의 최대 사이즈 (기본 1MB)
spring.servlet.multipart.max-request-size=10MB // 멀티파트 요청 하나에서 여러 파일에 대한 전체 합 용량 (기본 10MB)

멀티파트는 일반적인 폼 요청에 비해 패킷 구성이 굉장히 복잡하므로 스프링은 multipart자체를 다루지 않는 옵션도 제공한다.(spring.servlet.multipart.enabled) 이 옵션에 false(기본=true)를 주면 서블릿 컨테이너는 멀티파트와 관련된 처리를 절대 하지 않는다.

이 옵션이 true인 동안에는 DispatcherServlet은 멀티파트 리졸버를 항상 실행한다. 멀티파트 리졸버는 요청이 멀티파트일 경우 여러가지 다른 처리를 진행한다(HttpServletRequest -> MultipartHttpServletRequest로 주입 : 멀티파트와 관련된 추가 기능 제공)

업로드 파일이 저장될 공간

// application.properties
file.dir=파일 업로드 경로 설정

어플리케이션 설정에서 file.dir경로를 설정하고 서블릿을 이용하여 우리는 업로드된 파일 데이터를 읽어 해당 경로에 저장할 수 있다.

@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);

        for (Part part : parts) {
            log.info("====== PART ======");
            log.info("name={} ", part.getName());
            Collection<String> headerNames = part.getHeaderNames();
            for (String headerName : headerNames) {
                log.info("header {}: {}", headerName, part.getHeader(headerName));
            }

            //편의 메서드
            //content-disposition
            log.info("SubmittedFileName", 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";
    }

request.getParts()로 하여금 멀티파트 데이터들을 받을 수 있다. 하나의 멀티파트 요청에 대해 여러 데이터가 존재할 수 있으므로 Part라는 객체를 Collections에 담아준다.

여기서 파일에 해당하는 Part를 찾기 위해 여러 메서드를 활용할 수 있다.

  • part.getName() : 멀티파트 요청에서 현재 파트의 이름을 반환한다. 보통 name 속성값을 가져오는 데 사용된다.
  • part.getHeaderNames() : 현재 파트의 모든 헤더 이름을 Collection<String> 형태로 반환한다. 이를 통해 해당 파트에 포함된 모든 헤더의 이름을 확인할 수 있다.
  • part.getHeader(String name) : 주어진 헤더 이름에 해당하는 헤더 값을 반환한다. 예를 들어, part.getHeader("Content-Type")을 호출하면 현재 파트의 Content-Type 헤더 값을 얻을 수 있다.
  • part.getSubmittedFileName() : 클라이언트가 업로드한 파일의 원본 이름을 반환한다. 주로 파일 업로드 시 사용되며, 사용자가 선택한 파일의 이름을 확인할 수 있다.
  • part.getInputStream() : 현재 파트의 내용을 읽을 수 있는 InputStream 객체를 반환한다. 파일 데이터나 텍스트 데이터를 읽어오기 위해 사용되며, 이 스트림을 통해 파일 내용을 처리할 수 있다.

Spring MultiPart

스프링으로 하여금 이러한 멀티파트 데이터를 조금 더 편리하게 이용할 수 있다. @RequestParam MultipartFile file으로 하여금 멀티파트에서 파일을 따로 받을 수 있다.

주요 메서드는 다음과 같다.

  • file.getOriginalFilename() : 업로드 파일 명
  • file.transferTo(...) : 파일 저장
profile
자바집사의 거북이 수련법

0개의 댓글