새발블로그
[Spring] 파일 업로드 & 다운로드 본문
1. 기본 파일 업로드 구조
Spring에서는 MultipartFile을 사용해 업로드된 파일을 처리합니다.
HTML form 태그에서 반드시 enctype="multipart/form-data"를 지정해야 합니다.
<form method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">Upload</button>
</form>
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) throws IOException {
if (!file.isEmpty()) {
String originalName = file.getOriginalFilename();
file.transferTo(new File("/upload/dir/" + originalName));
}
return "success";
}
2. 기본 파일 다운로드 구조
다운로드는 ResponseEntity<Resource>를 이용해 구현합니다.
@GetMapping("/download/{filename}")
public ResponseEntity<Resource> download(@PathVariable String filename) throws IOException {
Path path = Paths.get("/upload/dir/" + filename);
Resource resource = new InputStreamResource(Files.newInputStream(path));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
3. 파일명 충돌 방지와 UID(고유 ID)
업로드에서 중요한 포인트 중 하나는 파일명 충돌 방지입니다.
여러 사용자가 동일한 이름(image.png)으로 업로드할 경우를 대비해야 합니다.
-> 해결책 : UUID (Universally Unique Identifier)
String originalFilename = file.getOriginalFilename();
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
String uid = UUID.randomUUID().toString(); // 고유 UID 생성
String savedName = uid + extension;
왜 UID가 중요한가?
- 파일 고유 식별자: 파일 시스템뿐만 아니라 API 호출 시에도 안정적으로 파일을 식별 가능
- 외부 API 연동: CLOVA OCR 같은 서비스는 업로드 시 uid 필드를 요구하는데, 이는 요청을 구분하기 위한 필수 식별값
- DB 관리 용이: 원본 파일명과 별개로 UID를 저장하면 충돌 없이 안정적 관리 가능
예시 DB 스키마:
| 칼럼 | 설명 |
| id (PK) | DB 내부 식별자 |
| uid | UUID 기반 고유값 |
| original_name | 사용자가 업로드한 원본 이름 |
| saved_path | 실제 저장된 경로 |
| size | 파일 크기 |
4. 업로드 디렉토리 구조화
파일이 많아지면 관리가 어려워집니다.
-> 날짜별 디렉토리 분리 방식 권장 (/2025/09/21/uid.png)
public static String makeFolderByDate(String baseDir) {
String folderPath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
Path uploadPath = Paths.get(baseDir, folderPath);
Files.createDirectories(uploadPath);
return uploadPath.toString();
}
5. 업로드 예외 처리와 롤백
문제:
- 파일은 디스크에 저장됨
- DB INSERT는 MyBatis로 처리됨
- 중간에 예외 발생 시 DB는 @Transactional로 롤백되지만, 파일은 남아버림 ⚠️
해결책: 업로드 성공 파일을 List에 저장해두고, 예외 발생 시 직접 삭제.
@Transactional
public void upload(Long bno, List<MultipartFile> files) {
List<String> uploadedPaths = new ArrayList<>();
try {
for (MultipartFile file : files) {
String path = UploadFiles.upload(BASE_DIR, file);
uploadedPaths.add(path);
BoardAttachmentVO attach = BoardAttachmentVO.of(file, bno, path);
mapper.createAttachment(attach);
}
} catch (Exception e) {
// 업로드된 파일 삭제
for (String path : uploadedPaths) {
Files.deleteIfExists(Paths.get(path));
}
throw new RuntimeException("업로드 실패", e); // rollback 유도
}
}
'Server > Spring' 카테고리의 다른 글
| [Spring] AOP (Aspect Oriented Programming) (0) | 2025.09.22 |
|---|---|
| [Spring] 직렬화와 역직렬화 (0) | 2025.09.22 |
| [Spring] Spring + MyBatis (0) | 2025.09.22 |
| [Spring] Spring 어노테이션 (0) | 2025.07.11 |
| [Spring] Spring MVC (0) | 2025.07.08 |