[1] 파일 업로드 구현
[1-1] application.properties 설정
spring.servlet.multipart.max-request-size=30MB
spring.servlet.multipart.max-file-size=10MB
org.zerock.upload.path=upload
[1-2] ProductDTO
@Data
@NoArgsConstructor
@Builder
@AllArgsConstructor
public class ProductDTO {
private Long pno;
private String pname;
private int price;
private String pdesc;
private boolean delFlag; // 실제 삭제가 아니라 삭제인 것처럼
@Builder.Default
private List<MultipartFile> files = new ArrayList<>();
@Builder.Default
private List<String> uploadedFileNames = new ArrayList<>();
}
[1-3] CustomFileUtil -- 사용자가 업로드한 파일을 서버의 특정 폴더(uploadPath)에 저장함
@Component
@Log4j2
@RequiredArgsConstructor //의존성 주입
public class CustomFileUtil {
@Value("${org.zerock.upload.path}")
private String uploadPath;
@PostConstruct
public void init(){
File tempFolder = new File(uploadPath);
if(!tempFolder.exists()){
tempFolder.mkdir();
}
uploadPath = tempFolder.getAbsolutePath();
log.info("-----------");
log.info(uploadPath);
}
public List<String> saveFiles(List<MultipartFile> files) throws RuntimeException{
if(files == null || files.size() ==0){
return List.of(); //빈 거 반환
}
List<String> uploadNames = new ArrayList<>();
for(MultipartFile file: files){
String savedName = UUID.randomUUID().toString()+"_"+file.getOriginalFilename();
Path savePath = Paths.get(uploadPath, savedName);
try {
Files.copy(file.getInputStream(), savePath); // file 저장
uploadNames.add(savedName);
} catch (IOException e) {
throw new RuntimeException(e);
}
} //end for
return uploadNames;
}
}
@Component: 다른 클래스에서 @Autowired나 @RequiredArgsConstructor 등을 통해 의존성을 주입받음
@PostConstruct: 스프링이 이 클래스를 생성한 후 자동으로 실행되는 초기화 메서드
uploadNames 리스트에 저장된 파일 이름을 저장함
UUID.randomUUID().toString(): 중복되지 않는 파일명 생성
Files.copy(file.getInputStream(), savePath)로 실제 파일을 저장
[1-4] 파일 업로드 확인
@RestController
@Log4j2
@RequiredArgsConstructor
@RequestMapping("/api/products")
public class ProductController {
private final CustomFileUtil fileUtil;
@PostMapping("/")
public Map<String, String> register(ProductDTO productDTO){
log.info("register: "+productDTO);
List<MultipartFile> files = productDTO.getFiles();
List<String> uploadedFileNames = fileUtil.saveFiles(files);
productDTO.setUploadedFileNames(uploadedFileNames);
log.info(uploadedFileNames);
return Map.of("RESULT", "SUCCESS");
}
}
[1-5] 썸네일 생성
build gradle에 implement
implementation group: 'net.coobird', name: 'thumbnailator', version: '0.4.19'
try {
Files.copy(file.getInputStream(), savePath); // 원본 file 저장
//thumbnail 생성
String contentType = file.getContentType(); //mime type
//이미지 파일이면
if(contentType != null || contentType.startsWith("image")){
Path thumbnailPath = Paths.get(uploadPath, "s_"+savedName);
Thumbnails.of(savePath.toFile()).size(200,200).toFile(thumbnailPath.toFile());
}
uploadNames.add(savedName);
} catch (IOException e) {
throw new RuntimeException(e);
}
[2] 파일 조회
[2-1] CustomFileUtil
public ResponseEntity<Resource> getFile(String fileName){
Resource resource = new FileSystemResource(uploadPath + File.separator + fileName);
if(!resource.isReadable()){
resource = new FileSystemResource(uploadPath+File.separator+"b96946e0-2fd5-4d44-ae6e-eb9266c64250_갱얼쥐.jpg");
}
HttpHeaders headers = new HttpHeaders();
try {
headers.add("Content-Type", Files.probeContentType(resource.getFile().toPath()));
} catch(IOException e){
throw new RuntimeException(e);
}
return ResponseEntity.ok().headers(headers).body(resource);
}
[2-2] ProductController
@GetMapping("/view/{fileName}")
public ResponseEntity<Resource> viewFileGet(@PathVariable("fileName") String fileName){
return fileUtil.getFile(fileName);
}
파일 이름 앞에 s_ 있으면 썸네일 파일 확인 가능
Files.probeContentType(resource.getFile().toPath()): 파일의 MIME 타입을 자동으로 감지하여 "Content-Type" 헤더에 추가
ex) .jpg, .png → image/jpeg, image/png
.pdf → application/pdf
.txt → text/plain
브라우저가 Content-Type을 보고 파일을 이미지로 표시할지, 다운로드할지 결정함
[3] 파일의 이름을 찾아서 삭제
public void deleteFiles(List<String> fileNames) {
if (fileNames == null || fileNames.size() == 0) {
return;
}
fileNames.forEach(fileName -> {
String thumbnailFileName = "s_" + fileName;
Path thumbnailPath = Paths.get(uploadPath, thumbnailFileName);
Path filePath = Paths.get(uploadPath, fileName);
try {
Files.deleteIfExists(filePath);
Files.deleteIfExists(thumbnailPath);
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
});
}
'Spring' 카테고리의 다른 글
[9] 상품 생성 (0) | 2025.03.18 |
---|---|
[7] 소셜 로그인 구현 (0) | 2025.03.12 |
[6] Todo get, register, modify, remove (0) | 2025.03.07 |
[5] 페이지 처리 controller 동작 (0) | 2025.03.06 |
[4] 페이지 처리하는 DTO 설계 (0) | 2025.03.05 |