[spring] 파일 업/다운로드
HTML 폼 전송 방식
HTML에서 폼을 전송하는 방식(Content-Type)에는 다음 두가지가 있다.
application/x-www-form-urlencoded : 문자와 같은 데이터를 키와 함께 전송하는 방식
- ex) username=Kim&age=20
multipart/form-data : 여러 개의 데이터를 파트별로 나누어 전송하는 방식. 파일과 같은 바이너리 데이터를 전송할 때 쓰임.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Content-Type: multipart/form-data; boundary=------XXX ------XXX Content-Disposition: form-data; name="username" kim ------XXX Content-Disposition: form-data; name="age" 20 ------XXX Content-Disposition: form-data; name="file"; filename="intro.png" Content-Type: Image/png [이미지 바이너리 데이터] ------XXX--
파일 업로드
서블릿 파일 업로드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@PostMapping("/upload")
public String saveFileV1(HttpServletRequest request) throws ServletException, IOException {
Collection<Part> parts = request.getParts(); // 멀티파트 데이터 가져오기
for (Part part : parts) {
//데이터 읽기
InputStream inputStream = part.getInputStream();
String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
//파일에 저장하기
if (StringUtils.hasText(part.getSubmittedFileName())) {
String fullPath = fileDir + part.getSubmittedFileName(); // 클라이언트가 전달한 파일명
part.write(fullPath); // 주어진 위치에 저장
}
}
return "upload-form";
}
request.getPart()
를 통해 멀티파트 데이터를 가져오고, 각 파트별로 part.write()
메서드를 사용하면 인자로 넣은 주소에 저장된다.
하지만 위와 같은 방법은 ` HttpServletRequest`를 인자로 받아와야하며, 파일을 각자 구분하기 위해 여러 코드를 넣어야한다.
스프링 파일 업로드
1
2
3
4
5
6
7
8
9
@PostMapping("/upload")
public String saveFile(@RequestParam String itemName, @RequestParam MultipartFile file) throws IOException {
if (!file.isEmpty()) {
String fullPath = fileDir + file.getOriginalFilename(); // 파일 원제목 가져오기
file.transferTo(new File(fullPath)); // 주어진 위치로 파일 저장
}
return "upload-form";
}
스프링에서는 멀티파트 파일을 저장하기 위핸 MultipartFile
를 제공한다. 따라서 매개변수의 이름을 들어올 파일의 이름과 맞춰준다면 자동으로 해당 매개변수에 파일을 넣어준다.
다음과 같이 @ModelAttribute
를 사용하여 받을 수도 있다.
1
2
3
4
5
6
7
8
9
// MultipartFile
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MultipartTest {
private String itemName;
private MultipartFile file;
}
1
2
@PostMapping("/upload")
public String saveFile(@ModelAttribute MultipartTest item) // work
파일 다운로드
이미지 다운로드
<img> 태그로 조회 시 내려주는 방법. UrlResource
로 이미지 파일을 읽어서 body에 넣어 반환한다.
1
2
3
4
5
@ResponseBody
@GetMapping("/images/{filename}")
public Resource downloadImage(@PathVariable String filename) throws MalformedURLException {
return new UrlResource("file:" + fileStore.getFullPath(filename));
}
파일 다운로드
Content-Disposition
헤더에 attachment; filename="파일 이름"
값을 넣고, UrlResource
를 body에 담아 내려준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@GetMapping("/attach/{itemId}")
public ResponseEntity<Resource> downloadAttach(@PathVariable Long itemId) throws MalformedURLException {
// db에 저장된 파일 데이터 가져오기.
Item item = itemRepository.findById(itemId);
String storeFileName = item.getAttachFile().getStoreFileName();
String uploadFileName = item.getAttachFile().getUploadFileName();
UrlResource resource = new UrlResource("file:" + fileStore.getFullPath(storeFileName));
String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + encodedUploadFileName + "\"")
.body(resource);
}
이미지 다운로드 때와 다른 점은 Content-Disposition
헤더가 설정되었다는 점이다.
Content-Disposition은 HTTP response body에 오는 컨텐츠의 기실/성향을 알려주는 속성으로, default 값은 web에 전달되는 data라고 생각하면된다.
특수한 경우는 Content-Disposition에 attachment를 주는 경우로, 이때 filename과 함께 주면 body에 오는 값을 다운로드 받으라는 뜻이 된다.
https://lannstark.tistory.com/8
스프링 멀티파트 설정
다음과 같이 application.properties
에서 멀티파트 데이터에 관한 설정을 할 수 있다.
1
2
3
4
5
6
# 한 파일의 최대 용량
spring.servlet.multipart.max-file-size=5MB
# 한 multipart 요청의 최대 크기
spring.servlet.multipart.max-request-size=10MB
# multipart 처리를 할 것인가. (false 시 멀티파트 데이터를 처리하지 않음)
spring.servlet.multipart.enabled=true