Refactor: 이미지 zip 파일 임시 디렉토리 제거

This commit is contained in:
김용수 2024-09-29 15:43:06 +09:00
parent db54b11a5f
commit 9196472235

View File

@ -32,8 +32,10 @@ import java.nio.file.Paths;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
@Slf4j @Slf4j
@Service @Service
@ -87,25 +89,45 @@ public class ImageService {
* Zip 파일 처리 메서드 * Zip 파일 처리 메서드
*/ */
@CheckPrivilege(PrivilegeType.EDITOR) @CheckPrivilege(PrivilegeType.EDITOR)
public void uploadFolderWithImages(final MultipartFile folderOrZip, final Integer projectId, final Integer folderId) throws IOException { public void uploadFolderWithImages(final MultipartFile zipFile, final Integer projectId, final Integer folderId) throws IOException {
log.debug("파일 크기: {}, 기존 파일 이름: {} ", folderOrZip.getSize(), folderOrZip.getOriginalFilename()); log.debug("파일 크기: {}, 기존 파일 이름: {} ", zipFile.getSize(), zipFile.getOriginalFilename());
// 프로젝트 정보 가져오기 Project project = getProject(projectId); // 현재 프로젝트
Project project = getProject(projectId); Folder rootFolder = folderRepository.findById(folderId).orElse(null); // 부모 프로젝트
Folder parentFolder = getOrCreateFolder(folderId, projectId);
String originalFilename = folderOrZip.getOriginalFilename(); Path tmpDir = null;
if (originalFilename != null && originalFilename.endsWith(".zip")) { try {
// Zip 파일 처리 String originalFilename = zipFile.getOriginalFilename();
Path tempDir = Files.createTempDirectory("uploadedFolder"); if (originalFilename == null || !originalFilename.endsWith(".zip")) {
unzip(folderOrZip, tempDir.toString()); throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE, "ZIP 파일만 업로드 가능");
processFolderRecursively(tempDir.toFile(), parentFolder, project); }
} else {
// 압축 파일이 아닌 경우 (단일 폴더 또는 파일) // ZIP 파일 처리
File tempFolder = new File(System.getProperty("java.io.tmpdir"), originalFilename); String zipFolderName = originalFilename.substring(0, originalFilename.lastIndexOf("."));
folderOrZip.transferTo(tempFolder); // 파일을 임시 디렉토리에 저장 tmpDir = Files.createTempDirectory(zipFolderName);
// 폴더 또는 파일을 재귀적으로 탐색하여 저장
processFolderRecursively(tempFolder, parentFolder, project); unzip(zipFile, tmpDir.toString());
processFolderRecursively(tmpDir.toFile(), rootFolder, project);
} finally {
if (tmpDir != null) {
deleteDirectoryRecursively(tmpDir);
log.debug("임시 디렉토리 삭제 완료: {}", tmpDir);
}
}
}
private void deleteDirectoryRecursively(Path directory) throws IOException {
if (Files.exists(directory)) {
try (Stream<Path> paths = Files.walk(directory)) {
paths.sorted(Comparator.reverseOrder()) // 하위 파일부터 삭제
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
log.error("Failed to delete file: {}", path, e);
}
});
}
} }
} }
@ -113,16 +135,23 @@ public class ImageService {
* 폴더 파일을 재귀적으로 탐색하여 처리 * 폴더 파일을 재귀적으로 탐색하여 처리
*/ */
private void processFolderRecursively(File directory, Folder parentFolder, Project project) { private void processFolderRecursively(File directory, Folder parentFolder, Project project) {
if (directory.exists() && directory.isDirectory()) { log.debug("폴더 이름 {}, 부모 폴더 이름 {}", directory.getName(), parentFolder == null ? "root" : parentFolder.getTitle());
Folder currentFolder = createFolderAndSave(directory.getName(), parentFolder, project);
if (directory.exists() && directory.isDirectory()) {
File[] files = directory.listFiles(); File[] files = directory.listFiles();
if (files != null) { if (files != null) {
for (File file : files) { for (File file : files) {
// 숨겨진 파일이나 __MACOSX 폴더를 제외
if (file.getName().startsWith("._") || file.getName().contains("__MACOSX")) {
log.debug("숨겨진 파일이나 __MACOSX 폴더 제외: {}", file.getName());
continue;
}
if (file.isDirectory()) { if (file.isDirectory()) {
Folder currentFolder = createFolderAndSave(file.getName(), parentFolder, project);
processFolderRecursively(file, currentFolder, project); processFolderRecursively(file, currentFolder, project);
} else if (isImageFile(file)) { } else if (isImageFile(file)) {
uploadAndSave(file, currentFolder, project); uploadAndSave(file, parentFolder, project);
} }
} }
} }
@ -198,15 +227,23 @@ public class ImageService {
// Apache Commons Compress 라이브러리를 사용하여 ZIP 파일을 처리 // Apache Commons Compress 라이브러리를 사용하여 ZIP 파일을 처리
private void unzip(MultipartFile zipFile, String destDir) throws IOException { private void unzip(MultipartFile zipFile, String destDir) throws IOException {
log.debug("Unzip 시작 {} ", zipFile.getOriginalFilename());
log.debug(System.getProperty("java.io.tmpdir"));
try (ZipArchiveInputStream zis = new ZipArchiveInputStream(zipFile.getInputStream(), "MS949")) { try (ZipArchiveInputStream zis = new ZipArchiveInputStream(zipFile.getInputStream(), "MS949")) {
ArchiveEntry entry; ArchiveEntry entry;
while ((entry = zis.getNextEntry()) != null) { while ((entry = zis.getNextEntry()) != null) {
ZipArchiveEntry zipEntry = (ZipArchiveEntry) entry; ZipArchiveEntry zipEntry = (ZipArchiveEntry) entry;
// 파일인지 확인한다.
Path newPath = zipSlipProtect(zipEntry, Paths.get(destDir)); Path newPath = zipSlipProtect(zipEntry, Paths.get(destDir));
// 디렉토리면 해당 디렉토리 이름으로 생성
if (zipEntry.isDirectory()) { if (zipEntry.isDirectory()) {
Files.createDirectories(newPath); Files.createDirectories(newPath);
} else { }
// 파일이면 이름을 기반으로 부모 폴더를 찾고 저장한다.
else {
Files.createDirectories(newPath.getParent()); Files.createDirectories(newPath.getParent());
try (OutputStream os = Files.newOutputStream(newPath)) { try (OutputStream os = Files.newOutputStream(newPath)) {
IOUtils.copy(zis, os); IOUtils.copy(zis, os);
@ -351,7 +388,6 @@ public class ImageService {
public Image createImage(String fileName, String key, Folder folder) { public Image createImage(String fileName, String key, Folder folder) {
String extension = getExtension(fileName); String extension = getExtension(fileName);
log.debug("이미지 업로드 이름 :{}", fileName);
return Image.of(fileName, key, extension, folder); return Image.of(fileName, key, extension, folder);
} }
} }