➡️
이 게시글은 [ 스프링 MVC 2편 - 섹션 11. 파일 업로드 (인프런 - 김영한 강사님)] 강의를 바탕으로 제작하였습니다.
1) 폼 데이터 전송 방식
2) 서블릿을 이용한 파일 업로드 방식
3) 스프링을 이용한 파일 업로드 방식
🚀 1. HTML 폼 데이터 전송 방식
1) application/x-www-form-urlencoded
- 사용방식: HTML 폼 데이터를 서버로 전송하는 가장 기본적인 방법이다.
- 전송할 항목을 HTTP Body에 문자로 username=kim&age=20 과 같이 &로 구분해서 전송한다.
- 문제점 : 이름, 나이, 첨부파일을 같이 전송해야 하는 경우, 이름, 나이는 문자로, 첨부파일은 바이너리로 전송해야 하므로 문자와 바이너리를 동시에 전송해햐 한다.
2) multipart/form-data
- 사용방식: Form 태그에 별도의 enctype="multipart/form-data" 를 지정해야 한다.
multipart/form-data 방식은 다른 종류의 여러 파일과 폼의 내용 함께 전송할 수 있다.
이 장 마지막에 여러 종류의 파일을 한번에 업로드하는 예제를 다루면서 활용해보자.
🚀 2. 서블릿과 스프링 MVC를 이용한 파일 업로드
🔹 서블릿 기반 파일 업로드
서블릿이 제공하는 Part를 이용한다.
(참고 : multipart/form-data 형식으로 전송된 데이터는 request.getParameter()와 같은 메서드로 접근할 수 없다고 한다.
따라서 HttpServletRequest의 getPart() 메서드를 이용해서 접근해야 한다.)
application.properties에
file.dir=파일 업로드 경로 설정(예): /Users/username/Desktop/study/file/
다음과 같이 내가 파일을 저장할 경로를 입력한다.
ServletUploadController.class를 만들어서 사용자가 파일을 제출하면 itemName과 request.getParts()를 받는다.
parts에 저장된 part 들은 headerNames와 데이터를 저장하고 있다.
part.getHeaderNames(), part.getInputStream을 통해 headerNames, 데이터를 받고
part.write(저장할 경로 명)을 통해 데이터를 저장한다.
문제점: 서블릿이 제공하는 Part를 쓰면 이 전 방식보단 편하지만 HttpServletRequest를 사용해야 하고, 파일 부분만 구분하려면 여러가지 코드를 넣어야 한다. 스프링은 더 편한 MultipartFile 인터페이스를 제공한다.
스프링 - MultipartFile
내 기준 핵심
1. 파일을 업로드할 때는 MultipartFile을 사용한다.
위에 서블릿 part 했을 때처럼 복잡하게 할거가 없고, @RequestParam MultipartFile file을 파라미터로 받아서 바로 여러 함수를 써먹어도 다 된다.
우선 다음 코드를 통해 서블릿 Part를 이용한 코드와 스프링 MultipartFile을 이용한 코드를 비교해보자.
위에가 서블릿 Part, 밑에가 스프링 MultipartFile을 이용한 코드이다.
//서블릿을 이용한 파일 저장 메소드
@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; filename
log.info("submittedFileName={}", part.getSubmittedFileName());
log.info("size={}", part.getSize()); //part body size
//데이터 읽기
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";
}
-------------------------------------------------------------------------------------------
//스프링을 제공한 파일 저장 메소드
@PostMapping("/upload")
public String saveFile(@RequestParam String itemName,
@RequestParam MultipartFile file, HttpServletRequest request) throws IOException {
log.info("request={}", request);
log.info("itemName={}", itemName);
log.info("multipartFile={}", file);
if (!file.isEmpty()) {
String fullPath = fileDir + file.getOriginalFilename();
log.info("파일 저장 fullPath={}", fullPath);
file.transferTo(new File(fullPath));
}
return "upload-form";
}
🚀 3. 실제 애플리케이션에서 파일 업로드 구현 예제
🔹 예제
실제 이미지 파일과 텍스트 파일을 업로드, 다운로드 해보자.
내 기준 핵심
1. uploadFileName, storeFileName은 따로 생성해주고, storeFileName은 UUID로 만들어야 하며, 고객이 다시 다운로드할 때는 uploadFileName.확장자로 다운로드할 수 있게 하자.
2. extractExt메서드를 통해 확장자를 붙여주자.
3. 나머지도 중요하나 대부분 구현하는 방법.
🔹 요구사항
- 상품이름
- 첨부파일 하나
- 이미지 파일 여러 개
🔹 방법
- 상품 도메인과 상품 리포지토리를 만들어준다. 그 후, UploadFile.class를 만들어준다.
(여기서 uploadFileName과 storeFileName을 각각 생성해줘야 한다. 고객이 업로드한 파일명인 uploadFileName만 생성해주면 안된다. 서로 다른 고객이 같은 파일 이름으로 업로드하는 경우 기존 파일 이름과 충돌이 날 수 있다.) - 따라서 고객이 업로드한 파일명과는 다른, 내부에서 관리하는 별도의 파일명이 필요하다. 파일명이 절대로 안 겹치게 하는 것이 목표이므로 UUID.randomUUID() 를 사용하여 파일명을 랜덤으로 생성한다.
- 또한 서버에서는 파일명을 겹치지 않게 해야하나 다운로드시에는 고객이 업로드한 파일 이름으로 다운로드하는게 좋으므로 @GetMapping("/attach/{itemId}") 에서 Content-Disposition 해더에 attachment; filename="업로드 파일명" 값을 준다. )
- 멀티파트 파일을 서버에 저장하는 FileStore.class를 보면 storeFiles, storeFile 메서드는 서블릿 part를 다룬 방법과 비슷해서 설명을 생략한다.
- 새롭게 보이는 메서드로 extractExt 메서드가 있다. 업로드한 파일명 중 .이후에 붙은 확장자를 파일명에 붙여주기 위함이다.
private String createStoreFileName(String originalFilename) {
String ext = extractExt(originalFilename);
String uuid = UUID.randomUUID().toString();
return uuid + "." + ext;
}
'개발공부 > 백엔드' 카테고리의 다른 글
스프링 MVC 2편 - 검증(Validation) - 2 (2) | 2025.01.20 |
---|---|
스프링 MVC 2편 - 검증(Validation) - 1 (0) | 2025.01.17 |
스프링 MVC 2편 - 메시지, 국제화 (0) | 2025.01.13 |
스프링 MVC 2편 - 타임리프 (Thymeleaf) (0) | 2025.01.10 |
스프링 MVC 1편 - 스프링MVC - 구조 이해 -2 (0) | 2025.01.08 |