[Spring] 파일 업로드

hi·2022년 12월 18일
0

HTML 폼 전송 방식

1. application/x-www-form-urlencoded

  • HTML 폼 데이터를 서버로 전송하는 가장 기본적인 방법
  • Form 태그에 별도의 enctype 옵션이 없으면 웹 브라우저는 요청 HTTP 메시지의 헤더에 다음 내용을 추가
    Content-Type: application/x-www-form-urlencoded
    그리고 폼에 입력한 항목을 HTTP Body에 추가하여 전송
    ex) username=kim&age=20
  • but, 파일을 업로드 하려면 바이너리 데이터를 전송해야 하는데
    이 방법은 문자를 전송하는 방식.
    또한 문자와 바이너리를 동시에 전송하게 되므로 어렵다

2. multipart/form-data

  • Form 태그에 별도의 enctype="multipart/form-data" 를 지정하여 사용
  • 다른 종류의 여러 파일과 폼의 내용을 함께 전송 가능
  • 요청 메시지를 보면 각 전송 항목이 구분되어 있다

서블릿을 통한 파일 업로드

request.getParts()

: multipart/form-data 전송 방식에서 각각 나누어진 부분을 받아서 확인


참고 옵션들

HTTP 요청 메시지 확인
logging.level.org.apache.coyote.http11=debug

업로드 사이즈 제한
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=10MB

max-file-size : 파일 하나의 최대 사이즈, 기본 1MB
max-request-size : 멀티파트 요청 하나에 여러 파일을 업로드 할 수 있는데, 그 전체 합

멀티파트 처리
spring.servlet.multipart.enabled=false

  • 옵션을 끄면 request.getParameter("itemName") , request.getParts()의 결과도 X
    기본 true
  • 옵션을 켜면 스프링의 DispatcherServlet에서 멀티파트 리졸버(MultipartResolver) 실행
  • 멀티파트 리졸버는 멀티파트 요청시 HttpServletRequest를
    MultipartHttpServletRequest로 변환해서 반환
    (MultipartHttpServletRequest는 HttpServletRequest 상속, 멀티파트 관련 기능 추가 제공)

Part

파일 업로드 경로 설정

//application.properties 

file.dir=C: /Users/study/file/
  • 마지막 / 포함 주의
@Value("${file.dir}")
private String fileDir;
  • application.properties에서 설정한 경로를 가져와 사용 가능

주요 메서드

part.getSubmittedFileName() : 클라이언트가 전달한 파일명
part.getInputStream() : Part의 전송 데이터 읽기
part.write() : Part를 통해 전송된 데이터 저장


서블릿이 제공하는 Part는 편하기는 하지만
HttpServletRequest 를 사용해야 하고 추가로 파일 부분만 구분하려면 여러가지 코드가 필요하다


스프링의 파일 업로드

MultipartFile 인터페이스로 멀티파트 파일을 편리하게 지원

@PostMapping("/upload")
public String saveFile(@RequestParam String itemName,
                       @RequestParam MultipartFile file) throws IOException {

}

@RequestParam MultipartFile file

  • 업로드하는 HTML Form의 name에 맞추어 @RequestParam 을 적용
  • @ModelAttribute에서도 동일하게 사용 가능

주요 메서드

file.getOriginalFilename() : 업로드 파일 명
file.transferTo() : 파일 저장

🔎

@Data
public class Item {

    private Long id;
    private String itemName;
    private UploadFile attachFile; //첨부파일
    private List<UploadFile> imageFiles;
}
  • 파일 자체를 DB에 저장하지 않음
    기본 베이스 경로 이후의 상대적인 경로만 저장

public class UploadFile {
	private String uploadFileName; //고객이 업로드한 파일명
	private String storeFileName; //서버 내부에서 관리하는 파일명
}
  • 다른 고객이 같은 파일명을 사용할 경우를 대비하여 내부에서는 다른 이름으로 관리

파일 다운로드

@GetMapping("/attach/{itemId}")
public ResponseEntity<Resource> downloadAttach(@PathVariable Long itemId) throws MalformedURLException { 

	Item item = itemRepository.findById(itemId);
    String storeFileName = item.getAttachFile().getStoreFileName();
    String uploadFileName = item.getAttachFile().getUploadFileName(); 

	//"file:/Users/../b3ab2107-0ae7-4355-a958-089cb2b3d9a1.gif"
    UrlResource resource = new UrlResource("file:" + fileStore.getFullPath(storeFileName));

	//인코딩
    String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8);
    String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\"";

    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) 
            .body(resource);
    }
  • 파일 다운로드시에는 고객이 업로드한 파일 이름 사용
    Content-Disposition 헤더에 attachment; filename="업로드 파일명" 값 설정

0개의 댓글