From 423b9977e1adca2664610263ed28f01b21f71a52 Mon Sep 17 00:00:00 2001 From: kimtaesoo7 Date: Wed, 2 Oct 2024 13:32:47 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20Presigned=EB=A1=9C=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20=20-=20S1?= =?UTF-8?q?1P21S002-249?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/controller/ImageController.java | 3 +- .../domain/image/service/ImageService.java | 32 +++++++++++++++++++ .../global/service/S3UploadService.java | 21 +++++++++++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/worlabel/domain/image/controller/ImageController.java b/backend/src/main/java/com/worlabel/domain/image/controller/ImageController.java index 35d8f68..660814a 100644 --- a/backend/src/main/java/com/worlabel/domain/image/controller/ImageController.java +++ b/backend/src/main/java/com/worlabel/domain/image/controller/ImageController.java @@ -61,8 +61,7 @@ public class ImageController { @PathVariable("project_id") final Integer projectId, @PathVariable("folder_id") final Integer folderId) { log.debug("requestImageList {}", imageMetaList); - // TODO: 변경 - return imageMetaList.stream().map(o -> ImagePresignedUrlResponse.of(o.getId(), o.getFileName())).toList(); + return imageService.uploadFolderByPresignedImage(memberId, imageMetaList, projectId, folderId); } @GetMapping("/folders/{folder_id}/images/{image_id}") diff --git a/backend/src/main/java/com/worlabel/domain/image/service/ImageService.java b/backend/src/main/java/com/worlabel/domain/image/service/ImageService.java index 6b3e212..8772d6a 100644 --- a/backend/src/main/java/com/worlabel/domain/image/service/ImageService.java +++ b/backend/src/main/java/com/worlabel/domain/image/service/ImageService.java @@ -38,6 +38,7 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; @@ -318,7 +319,38 @@ public class ImageService { } } + @Transactional + @CheckPrivilege(PrivilegeType.EDITOR) + public List uploadFolderByPresignedImage(final Integer memberId, + final List imageMetaList, + final Integer projectId, + final Integer folderId) { + Folder folder = getOrCreateFolder(folderId, projectId); + List presignedUrls = new ArrayList<>(); + + for (ImageMetaRequest meta : imageMetaList) { + // UUID 생성 및 이미지 Key 지정 + String uuid = UUID.randomUUID().toString(); + String fileName = meta.getFileName(); + String extension = getExtension(fileName); + String s3Key = uuid + "." + extension; + + // Presigned URL 생성 + String presignedUrl = s3UploadService.generatePresignedUrl(s3Key); + + // DB에 이미지 메타데이터 저장 + Image image = Image.of(fileName, s3Key, extension, folder); + imageRepository.save(image); + + // Presigned URL과 함께 응답 데이터 생성 + ImagePresignedUrlResponse response = ImagePresignedUrlResponse.of(meta.getId(), presignedUrl); + presignedUrls.add(response); + } + + return presignedUrls; + } // 이미지 가져오면서 프로젝트 소속 여부를 확인 + private Image getImageByIdAndFolderIdAndFolderProjectId(final Integer folderId, final Long imageId, final Integer projectId) { return imageRepository.findByIdAndFolderIdAndFolderProjectId(imageId, folderId, projectId) .orElseThrow(() -> new CustomException(ErrorCode.DATA_NOT_FOUND)); diff --git a/backend/src/main/java/com/worlabel/global/service/S3UploadService.java b/backend/src/main/java/com/worlabel/global/service/S3UploadService.java index 7ed27a8..cd41c79 100644 --- a/backend/src/main/java/com/worlabel/global/service/S3UploadService.java +++ b/backend/src/main/java/com/worlabel/global/service/S3UploadService.java @@ -1,5 +1,6 @@ package com.worlabel.global.service; +import com.amazonaws.HttpMethod; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.*; import com.amazonaws.util.IOUtils; @@ -19,6 +20,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.util.Date; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -68,7 +70,7 @@ public class S3UploadService { try (InputStream inputStream = file.getInputStream()) { return uploadImageToS3(inputStream, extension, projectId); } catch (IOException e) { - log.debug("MultipartFile 업로드 에러 발생 {}",file.getOriginalFilename(), e); + log.debug("MultipartFile 업로드 에러 발생 {}", file.getOriginalFilename(), e); throw new CustomException(ErrorCode.FAIL_TO_CREATE_FILE); } } @@ -152,4 +154,21 @@ public class S3UploadService { throw new CustomException(ErrorCode.INVALID_FILE_PATH); } } + + /** + * Presigned URL 생성 + */ + public String generatePresignedUrl(String s3Key) { + Date expiration = new Date(); + long expTimeMillis = expiration.getTime(); + expTimeMillis += 1000 * 60 * 10; // 10분간 유효 + expiration.setTime(expTimeMillis); + + GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, s3Key) + .withMethod(HttpMethod.PUT) // PUT 방식 + .withExpiration(expiration); + + URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest); + return url.toString(); + } }