참고
파일 업로드 소개
NOTE
HTML 폼 전송 방식
1.
application/x-www-form-urlencoded
2.
multipart/form-data
application/x-www-form-urlencoded 방식
NOTE
HTML 폼 데이터를 서버로 전송하는 가장 기본적인 방법!
쿼리파라미터 방식으로 전송됨
•
Form 태그에 별도의 enctype옵션이 없다면, 요청 HTTP헤더에 자동으로 Content-Type: application/x-www-form-urlencoded를 추가한다.
•
하지만 파일 업로드는 문자가 아닌 바이너리 데이터를 전송해야 한다. 또한, 보통 폼을 전송할 때 파일만 전송하지 않는다는 점을 해결해야 한다.
◦
문자와 바이너리를 동시에 전송 → multipart/form-data 사용
multipart/form-data
NOTE
form의 enctype에 multipart/form-data 추가 (각 파트마다 헤더와 바디를 가진다.)
•
이 방식은 다른 종류의 여러 파일과 폼 내용을 함께 전달할 수 있다! (multipart)
content-Disposition
•
항목별 헤더, 여기에 부가 정보가 포함된다.
•
이렇게 항목을 구분해서 한 번에 전송이 가능하다!
스프링과 파일 업로드
NOTE
서블릿 방식도 같이 소개하지만, 사실 스프링방식만 알아도 상관없으므로 생략한다..
Form(MultipartFile 타입) 받기!
@Data
public class ItemForm {
private Long itemId;
private String itemName;
private MultipartFile attachFile;
private List<MultipartFile> imageFiles;
}
Java
복사
DTO 파일
@PostMapping("/items/new")
public String saveItem(@ModelAttribute ItemForm form, RedirectAttributes redirectAttributes) throws IOException {
UploadFile attachFile = fileStore.storeFile(form.getAttachFile());
List<UploadFile> storeImageFiles = fileStore.storeFiles(form.getImageFiles());
// 데이터베이스에 저장
// ...
return "redirect:/items/{itemId}";
}
Java
복사
컨트롤러 파일
•
위와 같이 Form의 형식에 따라 DTO를 작성하고, 파일은 MultipartFile 타입으로 받는다.
MultipartFile 주요 메서드
•
file.getOriginalFilename() : 업로드 파일명
•
file.transferTo() : 파일 저장
예제로 구현하는 파일 업로드, 다운로드
NOTE
실제로 파일업로드 방식을 구현해보자!
요구사항
•
상품을 관리(상품 이름, 첨부파일 하나, 이미지파일 여러개)
•
첨부파일 업로드, 다운로드 기능
•
업로드한 이미지 웹 브라우저에서 확인
DTO
NOTE
@Data
public class ItemForm {
private Long itemId;
private String itemName;
private MultipartFile attachFile;
private List<MultipartFile> imageFiles;
}
Java
복사
파일은 MultipartFile로 받는다.
Entity
NOTE
@Data
public class Item {
private Long id;
private String itemName;
private UploadFile attachFile;
private List<UploadFile> imageFiles;
}
Java
복사
실제로 저장하는건 파일이름
@Data
public class UploadFile {
private String uploadFileName;
private String storeFileName;
public UploadFile(String uploadFileName, String storeFileName) {
this.uploadFileName = uploadFileName;
this.storeFileName = storeFileName;
}
}
Java
복사
데이터베이스에 저장되는 이름과, 업로드할때 이름은 달라야한다!
FileStore
NOTE
전체코드!
첨부파일 저장
// properties에 있는 경로값
@Value("${file.dir}")
private String fileDir;
// 파일을 어디다 저장할것인지
public String getFullPath(String filename) {
return fileDir + filename;
}
Java
복사
public UploadFile storeFile(MultipartFile multipartFile) throws IOException {
if (multipartFile.isEmpty()) {
return null;
}
// 저장할 파일명으로 변경
String originalFilename = multipartFile.getOriginalFilename();
String storeFileName = createStoreFileName(originalFilename);
// 파일저장
multipartFile.transferTo(new File(getFullPath(storeFileName)));
return new UploadFile(originalFilename, storeFileName);
}
Java
복사
파일 리스트 저장
public List<UploadFile> storeFiles(List<MultipartFile> multipartFiles) throws IOException {
List<UploadFile> storeFileResult = new ArrayList<>();
// 여러 파일 처리
for (MultipartFile multipartFile : multipartFiles) {
if(!multipartFile.isEmpty()){
UploadFile uploadFile = storeFile(multipartFile);
storeFileResult.add(uploadFile);
}
}
return storeFileResult;
}
Java
복사
업로드 파일명 → DB에 저장할 파일명 변경
// 저장할 파일이름 ex(UUID 랜덤값 + . + png )
private String createStoreFileName(String originalFilename) {
String uuid = UUID.randomUUID().toString();
String ext = extracted(originalFilename);
return uuid + "." + ext;
}
// .png 형식 살리기
private String extracted(String originalFilename) {
int pos = originalFilename.lastIndexOf(".");
return originalFilename.substring(pos + 1);
}
Java
복사
Controller
NOTE
전체코드!
이미지 반환
@ResponseBody
@GetMapping("/images/{filename}")
public Resource downloadImage(@PathVariable String filename) throws MalformedURLException {
return new UrlResource("file:" + fileStore.getFullPath(filename));
}
Java
복사
해당 컨트롤러가 있어야 이미지가 정상적으로 나옴
다운로드
@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();
// 파일 다운로드 위한 생성
UrlResource resource = new UrlResource("file:" + fileStore.getFullPath(storeFileName));
log.info("uploadFileName={}", uploadFileName);
// 파일명 인코딩 및 파일 다운로드 기능 추가
String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8);
String contentDisposition = "attach; filename=\"" + encodedUploadFileName + "\"";
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
.body(resource);
}
Java
복사
참고!
NOTE
MultipartFIle의 용량제한의 기본값은 1MB이다. 이 제한을 해제하려면 yaml에서 설정을 해주어야 한다.
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
YAML
복사