From 98dc47e55ee53895c8711ce504a363fb459305b9 Mon Sep 17 00:00:00 2001 From: 2020147542 Date: Wed, 19 Feb 2025 00:40:41 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Refactor:=20=ED=8C=8C=EC=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=A0=80=EC=9E=A5=20=EB=B0=A9=EB=B2=95=20=EB=A6=AC?= =?UTF-8?q?=ED=8E=99=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../codeplay/controller/FileController.java | 12 +-- .../java/umc/codeplay/domain/Harmony.java | 2 +- src/main/java/umc/codeplay/domain/Music.java | 3 +- src/main/java/umc/codeplay/domain/Remix.java | 2 +- src/main/java/umc/codeplay/domain/Track.java | 2 +- .../umc/codeplay/domain/enums/FileType.java | 38 +++---- .../umc/codeplay/service/FileService.java | 101 ++++++++++-------- 7 files changed, 76 insertions(+), 84 deletions(-) diff --git a/src/main/java/umc/codeplay/controller/FileController.java b/src/main/java/umc/codeplay/controller/FileController.java index d265b2b..c1d1ff3 100644 --- a/src/main/java/umc/codeplay/controller/FileController.java +++ b/src/main/java/umc/codeplay/controller/FileController.java @@ -15,8 +15,6 @@ import umc.codeplay.dto.FileResponseDTO; import umc.codeplay.service.FileService; -import static umc.codeplay.service.FileService.buildFilename; - @RestController @RequestMapping("/files") @RequiredArgsConstructor @@ -30,15 +28,9 @@ public class FileController { description = "업로드를 위한 Presigned URL 생성 - 유효시간 존재") @PostMapping("/upload") public ApiResponse generateUrl( - @RequestParam(value = "fileType") FileType fileType, - @RequestParam(value = "fileName") String fileName) { + @RequestParam FileType fileType, @RequestParam String fileName) { String username = SecurityContextHolder.getContext().getAuthentication().getName(); - String newFileName = fileType.getFolderName() + buildFilename(fileName); - - Long id = fileType.processUpload(fileService, newFileName, username); - String uploadUrl = fileService.generatePutPresignedUrl(newFileName); - - return ApiResponse.onSuccess(fileType.createResponse(uploadUrl, id)); + return ApiResponse.onSuccess(fileService.getUploadUrl(username, fileName, fileType)); } } diff --git a/src/main/java/umc/codeplay/domain/Harmony.java b/src/main/java/umc/codeplay/domain/Harmony.java index 05e4ff5..a4c99fb 100644 --- a/src/main/java/umc/codeplay/domain/Harmony.java +++ b/src/main/java/umc/codeplay/domain/Harmony.java @@ -41,7 +41,7 @@ public class Harmony extends BaseEntity { @Builder private Harmony(String scale, String genre, Integer bpm, String voiceColor, Music music) { - this.title = music.getTitle().split("-", 2)[1]; + this.title = music.getTitle() + "_화성분석 결과"; this.scale = scale; this.genre = genre; this.bpm = bpm; diff --git a/src/main/java/umc/codeplay/domain/Music.java b/src/main/java/umc/codeplay/domain/Music.java index 79e642b..d367885 100644 --- a/src/main/java/umc/codeplay/domain/Music.java +++ b/src/main/java/umc/codeplay/domain/Music.java @@ -26,7 +26,8 @@ public class Music extends BaseEntity { @Column(nullable = false, length = 100) private String title; - @Column(columnDefinition = "TEXT", nullable = false) + @Column(columnDefinition = "TEXT") + @Setter private String musicUrl; @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/java/umc/codeplay/domain/Remix.java b/src/main/java/umc/codeplay/domain/Remix.java index 9c10ffa..1745899 100644 --- a/src/main/java/umc/codeplay/domain/Remix.java +++ b/src/main/java/umc/codeplay/domain/Remix.java @@ -69,7 +69,7 @@ public Remix( Boolean isChorusOn, String resultMusicUrl, Music music) { - this.title = music.getTitle().split("-", 2)[1]; + this.title = music.getTitle() + "_리믹스 결과"; this.scaleModulation = scaleModulation; this.tempoRatio = tempoRatio; this.reverbAmount = reverbAmount; diff --git a/src/main/java/umc/codeplay/domain/Track.java b/src/main/java/umc/codeplay/domain/Track.java index a34ca28..086e500 100644 --- a/src/main/java/umc/codeplay/domain/Track.java +++ b/src/main/java/umc/codeplay/domain/Track.java @@ -47,7 +47,7 @@ public class Track extends BaseEntity { @Builder public Track( String vocalUrl, String instrumentalUrl, String bassUrl, String drumsUrl, Music music) { - this.title = music.getTitle().split("-", 2)[1]; + this.title = music.getTitle() + "_스템분리 결과"; this.vocalUrl = vocalUrl; this.instrumentalUrl = instrumentalUrl; this.bassUrl = bassUrl; diff --git a/src/main/java/umc/codeplay/domain/enums/FileType.java b/src/main/java/umc/codeplay/domain/enums/FileType.java index 4bbff7f..6ac1c9e 100644 --- a/src/main/java/umc/codeplay/domain/enums/FileType.java +++ b/src/main/java/umc/codeplay/domain/enums/FileType.java @@ -1,39 +1,29 @@ package umc.codeplay.domain.enums; import umc.codeplay.dto.FileResponseDTO; -import umc.codeplay.service.FileService; public enum FileType { AUDIO { - public String getFolderName() { - return "requestFiles/"; - } - - public Long processUpload(FileService fileService, String fileName, String username) { - return fileService.uploadMusic(fileName, username); - } - - public FileResponseDTO.UploadFile createResponse(String uploadUrl, Long id) { - return new FileResponseDTO.UploadFile(uploadUrl, id, null); + @Override + public String buildStoragePath(Long id, String fileName) { + return String.format("%s%d/%s", BASE_AUDIO_PATH, id, fileName); } }, IMAGE { - public String getFolderName() { - return "profileImgs/"; - } - - public Long processUpload(FileService fileService, String fileName, String username) { - return fileService.uploadProfile(fileName, username); - } - - public FileResponseDTO.UploadFile createResponse(String uploadUrl, Long id) { - return new FileResponseDTO.UploadFile(uploadUrl, null, id); + @Override + public String buildStoragePath(Long id, String fileName) { + return String.format("%s%d/%s", BASE_IMAGE_PATH, id, fileName); } }; - public abstract String getFolderName(); + private static final String BASE_AUDIO_PATH = "requestFiles/"; + private static final String BASE_IMAGE_PATH = "profileImgs/"; - public abstract Long processUpload(FileService fileService, String fileName, String username); + public abstract String buildStoragePath(Long id, String fileName); - public abstract FileResponseDTO.UploadFile createResponse(String uploadUrl, Long id); + public FileResponseDTO.UploadFile createResponse(String uploadUrl, Long id) { + return this == AUDIO + ? new FileResponseDTO.UploadFile(uploadUrl, id, null) + : new FileResponseDTO.UploadFile(uploadUrl, null, id); + } } diff --git a/src/main/java/umc/codeplay/service/FileService.java b/src/main/java/umc/codeplay/service/FileService.java index 93a3af7..f10d924 100644 --- a/src/main/java/umc/codeplay/service/FileService.java +++ b/src/main/java/umc/codeplay/service/FileService.java @@ -5,24 +5,25 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.presigner.S3Presigner; -import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; import umc.codeplay.apiPayLoad.code.status.ErrorStatus; import umc.codeplay.apiPayLoad.exception.handler.GeneralHandler; import umc.codeplay.domain.Member; import umc.codeplay.domain.Music; +import umc.codeplay.domain.enums.FileType; +import umc.codeplay.dto.FileResponseDTO; import umc.codeplay.repository.MemberRepository; import umc.codeplay.repository.MusicRepository; @Service @RequiredArgsConstructor public class FileService { - @Value("${s3.bucket}") private String bucketName; @@ -33,64 +34,72 @@ public class FileService { private final MusicRepository musicRepository; private final MemberRepository memberRepository; - // 타임스탬프_파일명 형식으로 파일 이름 저장 - public static String buildFilename(String filename) { - return String.format("%s_%s", System.currentTimeMillis(), sanitizeFileName(filename)); - } + @Transactional + public FileResponseDTO.UploadFile getUploadUrl( + String username, String fileName, FileType fileType) { + String sanitizedFileName = sanitizeFileName(fileName); + Long entityId = createFileEntity(username, sanitizedFileName, fileType); + String storagePath = fileType.buildStoragePath(entityId, sanitizedFileName); - // 특수 문자나 공백 등을 정리 - private static String sanitizeFileName(String fileName) { - String normalizedFileName = Normalizer.normalize(fileName, Normalizer.Form.NFC); - System.out.println(normalizedFileName); - return normalizedFileName.replaceAll("\\s+", "_").replaceAll("[^가-힣a-zA-Z0-9.\\-_]", "_"); + updateEntityUrl(entityId, storagePath, fileType); + String uploadUrl = generatePresignedUrl(storagePath); + return fileType.createResponse(uploadUrl, entityId); } - // S3에 파일을 업로드할 수 있는 Presigned URL 생성 - public String generatePutPresignedUrl(String fileName) { - try { - PutObjectRequest putObjectRequest = - PutObjectRequest.builder().bucket(bucketName).key(fileName).build(); - - PutObjectPresignRequest presignRequest = - PutObjectPresignRequest.builder() - .signatureDuration(Duration.ofMinutes(60)) - .putObjectRequest(putObjectRequest) - .build(); - - PresignedPutObjectRequest presignedRequest = - s3Presigner.presignPutObject(presignRequest); - return presignedRequest.url().toString(); - } catch (Exception e) { - throw new GeneralHandler(ErrorStatus.AWS_SERVICE_UNAVAILABLE); - } + private String sanitizeFileName(String fileName) { + return Normalizer.normalize(fileName, Normalizer.Form.NFC) + .replaceAll("[^가-힣a-zA-Z0-9.\\s\\-_]", "_"); } - // User 레포지토리에 업로드 - public Long uploadProfile(String newFileName, String userEmail) { + private Long createFileEntity(String userEmail, String fileName, FileType fileType) { Member member = memberRepository .findByEmail(userEmail) .orElseThrow(() -> new GeneralHandler(ErrorStatus.MEMBER_NOT_FOUND)); - String s3Url = - String.format("https://%s.s3.%s.amazonaws.com/%s", bucketName, region, newFileName); + if (fileType == FileType.IMAGE) { + return member.getId(); + } - member.setProfileUrl(s3Url); - return memberRepository.save(member).getId(); + Music newMusic = Music.builder().title(fileName).member(member).build(); + return musicRepository.save(newMusic).getId(); } - // music 레포지토리에 업로드 - public Long uploadMusic(String newFileName, String userEmail) { - Member member = - memberRepository - .findByEmail(userEmail) - .orElseThrow(() -> new GeneralHandler(ErrorStatus.MEMBER_NOT_FOUND)); + private void updateEntityUrl(Long id, String storagePath, FileType fileType) { + String url = + String.format("https://%s.s3.%s.amazonaws.com/%s", bucketName, region, storagePath); + + if (fileType == FileType.IMAGE) { + Member member = + memberRepository + .findById(id) + .orElseThrow(() -> new GeneralHandler(ErrorStatus.MEMBER_NOT_FOUND)); + member.setProfileUrl(url); + memberRepository.save(member); + } else { + Music music = + musicRepository + .findById(id) + .orElseThrow(() -> new GeneralHandler(ErrorStatus.MUSIC_NOT_FOUND)); + music.setMusicUrl(url); + musicRepository.save(music); + } + } - // 저장하는 url은 유효시간이 없는 public - String s3Url = - String.format("https://%s.s3.%s.amazonaws.com/%s", bucketName, region, newFileName); - Music newMusic = Music.builder().title(newFileName).musicUrl(s3Url).member(member).build(); + private String generatePresignedUrl(String storagePath) { + try { + PutObjectRequest objectRequest = + PutObjectRequest.builder().bucket(bucketName).key(storagePath).build(); - return musicRepository.save(newMusic).getId(); + PutObjectPresignRequest presignRequest = + PutObjectPresignRequest.builder() + .signatureDuration(Duration.ofMinutes(60)) + .putObjectRequest(objectRequest) + .build(); + + return s3Presigner.presignPutObject(presignRequest).url().toString(); + } catch (Exception e) { + throw new GeneralHandler(ErrorStatus.AWS_SERVICE_UNAVAILABLE); + } } } From e47f20f404c303e47a7a3297e632bcd4d94e92af Mon Sep 17 00:00:00 2001 From: 2020147542 Date: Wed, 19 Feb 2025 00:42:37 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Style:=20=EC=A3=BC=EC=84=9D=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/umc/codeplay/controller/TaskController.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/umc/codeplay/controller/TaskController.java b/src/main/java/umc/codeplay/controller/TaskController.java index 7c98175..e8f1418 100644 --- a/src/main/java/umc/codeplay/controller/TaskController.java +++ b/src/main/java/umc/codeplay/controller/TaskController.java @@ -1,16 +1,11 @@ package umc.codeplay.controller; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import lombok.RequiredArgsConstructor; +import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import umc.codeplay.apiPayLoad.ApiResponse; @@ -75,6 +70,7 @@ public ApiResponse getTask( return ApiResponse.onSuccess(MemberConverter.toTaskProgressDTO(task)); } + @Hidden // TODO: 기능 완성시 @Hidden 태그만 삭제해주세요! @Operation( summary = "작업 진행 상황 조회", description = "작업 ID를 받아 완료될 때까지 기다린 후 결과를 반환합니다. 기본 대기시간 5분, 10초마다 작업 상태확인.")