파일을 업로드 하려면 문자가 아닌 바이너리 데이터를 전송해야 한다.
또한 파일뿐 아니라 이름, 나이 등도 함께 전송해야 한다.
즉, 첨부파일은 바이너리로, 이름과 나이는 문자로 동시에 전송해야 한다.
그래서 사용하는게 multipart/form-data이다.
• 위와 같은 문제를 해결하기 위해 multipart/form-data라는 전송 방식을 제공한다.
• 방식을 사용하려면 Form 태그에 별도의 enctype="multipart/form-data" 를 지정해야 한다.
• 스프링은 MultipartFile 이라는 인터페이스로 멀티파트 파일을 매우 편리하게 지원한다.
• @ModelAttribute으로도 받을 수 있다.
@PostMapping("/profile")
public String modifyProfile(@AuthenticationPrincipal UserDetailsImpl userDetails, @ModelAttribute UserProfileRequestDto requestDto) throws IOException {
Long userId = userDetails.getUser().getId(); // 사용자 id
UserProfileResponseDto dto = userService.updateUserProfile(userId, requestDto);
return "redirect:/kp3c/user/profile"; // 수정 후 조회 화면에서 데이터 보여주기
}
• MultipartFile 주요 메서드
file.getOriginalFilename() : 업로드 파일 명
file.transferTo(new File("PATH")) : 파일 저장
import lombok.Data;
@Data
public class UploadFile {
private String uploadFileName;
private String storeFileName;
public UploadFile(String uploadFileName, String storeFileName) {
this.uploadFileName = uploadFileName;
this.storeFileName = storeFileName;
}
}
• 고객이 업로드한 파일명으로 서버 내부에 파일을 저장하면 안 된다.
• 똑같은 파일명으로 업로드하면 파일을 덮어버리기 때문!
• 그러기 때문에 서버에서는 저장할 파일명이 겹치지 않도록 내부에서 저장하는 별도의 파일명이 필요하다. (예를 들면 UUID를 사용해서 저장)
package hello.upload.file;
import hello.upload.domain.UploadFile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Component
public class FileStore {
@Value("${file.dir}") // application.properties에 사진 저장 경로 설정 (file.dir=/Users/choihyewon/Desktop/file/)
private String fileDir;
public String getFullPath(String filename) { //파일이름 받아서 fullpath를 반환
return fileDir + filename; // 디렉토리에 파일이름이 합쳐짐
}
// 루프를 돌면서 multipartFile 이 비어있지 않으면 실행하여 storeFileResult 에 넣어서 결과 반환
public List<UploadFile> storeFiles(List<MultipartFile> multipartFiles) throws IOException { // 여러 개 업로드
List<UploadFile> storeFileResult = new ArrayList<>(); // 업로드 파일이 계속 생성이 되기 때문에 담아줘야 한다.
for (MultipartFile multipartFile : multipartFiles) {
if (!multipartFile.isEmpty()) { // multipartFile 이 비어있지 않으면!
storeFileResult.add(storeFile(multipartFile));
// 위 코드를 두줄로 하면
UploadFile uploadFile = storeFile(multipartFile);
storeFileResult.add(uploadFile);
}
}
return storeFileResult;
}
// MultipartFile을 받아서 파일을 저장한 다음에 UploadFile로 반환해줌
public UploadFile storeFile(MultipartFile multipartFile) throws IOException { // 한 개 업로드
if (multipartFile.isEmpty()) {
return null;
}
String originalFilename = multipartFile.getOriginalFilename(); // 사용자가 업로드한 파일이름
String storeFileName = createStoreFileName(originalFilename); // 서버에 저장하는 파일명(uu아이디+.+확장자명)
multipartFile.transferTo(new File(getFullPath(storeFileName))); // 디렉토리에 파일이름이 합쳐진 것이 File로 만들어지고
return new UploadFile(originalFilename, storeFileName); // UploadFile 반환
}
private String createStoreFileName(String originalFilename) {
String ext = extractExt(originalFilename);
String uuid = UUID.randomUUID().toString(); // 서버에 저장하는 파일명(uu아이디)
return uuid + "." + ext; // ext : 확장자명
}
private String extractExt(String originalFilename) { // 확장자명 꺼내기
int pos = originalFilename.lastIndexOf("."); // 위치를 가져온다.
return originalFilename.substring(pos + 1); // . 다음에 있는 확장자명 꺼냄
}
}
• multipart/form-data을 사용하려면 form태그에 enctype="multipart/form-data"가 들어가야된다.
• input태그 속성으로 multiple="multiple" 을 넣으면 파일을 여러개 선택할 수 있다.
• 이때 스프링에선 List로 @RequestParam이든 @ModelAttribute든 받으면 된다.
• HTML에서 img src="/images/[파일이름]" 형식으로 쓰면 된다.
• UrlResource는 file: FULLPATH를 인자로 넘겨주면 실제 그곳에 있는 파일을 UrlResource형태로 리턴해준다. 그럼 HTML의 img에 보여지게 되는 것!
좋은 정보 얻어갑니다, 감사합니다.