From 6a32a0871ce61379416e0e0b4148b096e5c2e4ad Mon Sep 17 00:00:00 2001 From: junpakPark Date: Sun, 1 Oct 2023 15:29:24 +0900 Subject: [PATCH 1/7] =?UTF-8?q?refactor:=20S3Client.java=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD,=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84=EB=A6=AC,=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapbefine/image/application/S3ImageService.java | 10 +++++----- .../mapbefine/image/domain/ImageUploader.java | 12 ++++++++++++ .../S3Client.java => infrastructure/S3Uploader.java} | 10 ++++++---- 3 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java rename backend/src/main/java/com/mapbefine/mapbefine/image/{domain/S3Client.java => infrastructure/S3Uploader.java} (83%) diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java b/backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java index e9a9baf5..6bf7cfb1 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java @@ -1,6 +1,6 @@ package com.mapbefine.mapbefine.image.application; -import com.mapbefine.mapbefine.image.domain.S3Client; +import com.mapbefine.mapbefine.image.domain.ImageUploader; import com.mapbefine.mapbefine.image.domain.UploadFile; import java.io.IOException; import org.springframework.beans.factory.annotation.Value; @@ -14,17 +14,17 @@ public class S3ImageService implements ImageService { @Value("${prefix.upload.path}") private String prefixUploadPath; - private final S3Client s3Client; + private final ImageUploader imageUploader; - public S3ImageService(S3Client s3Client) { - this.s3Client = s3Client; + public S3ImageService(ImageUploader imageUploader) { + this.imageUploader = imageUploader; } @Override public String upload(MultipartFile multipartFile) { try { UploadFile uploadFile = UploadFile.from(multipartFile); - s3Client.upload(uploadFile); + imageUploader.upload(uploadFile); return getUploadPath(uploadFile); } catch (IOException exception) { throw new RuntimeException(exception); diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java new file mode 100644 index 00000000..5ba84060 --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java @@ -0,0 +1,12 @@ +package com.mapbefine.mapbefine.image.domain; + +import java.io.IOException; +import org.springframework.web.multipart.MultipartFile; + +public interface ImageUploader { + + void upload(MultipartFile multipartFile) throws IOException; + + void delete(String key); + +} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/S3Client.java b/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3Uploader.java similarity index 83% rename from backend/src/main/java/com/mapbefine/mapbefine/image/domain/S3Client.java rename to backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3Uploader.java index 595cebe5..0f5784e8 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/S3Client.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3Uploader.java @@ -1,8 +1,9 @@ -package com.mapbefine.mapbefine.image.domain; +package com.mapbefine.mapbefine.image.infrastructure; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.DeleteObjectRequest; import com.amazonaws.services.s3.model.PutObjectRequest; +import com.mapbefine.mapbefine.image.domain.ImageUploader; import java.io.File; import java.io.IOException; import java.util.Objects; @@ -11,16 +12,17 @@ import org.springframework.web.multipart.MultipartFile; @Component -public class S3Client { +public class S3Uploader implements ImageUploader { @Value("${s3.bucket}") private String bucket; private final AmazonS3 amazonS3; - public S3Client(AmazonS3 amazonS3) { + public S3Uploader(AmazonS3 amazonS3) { this.amazonS3 = amazonS3; } + @Override public void upload(MultipartFile multipartFile) throws IOException { File tempFile = null; @@ -45,8 +47,8 @@ private void removeTempFileIfExists(File tempFile) { } } + @Override public void delete(String key) { - // TODO 현재는 일단 기능만 만들어놓고, API 는 만들어놓지 않았습니다 회의를 통해서 결정해야 할 사항이 있는 것 같아서요! amazonS3.deleteObject(new DeleteObjectRequest(bucket, key)); } From 96fa36d0b8b6a322098ab26473cce44b22b7d925 Mon Sep 17 00:00:00 2001 From: junpakPark Date: Sun, 1 Oct 2023 15:30:59 +0900 Subject: [PATCH 2/7] =?UTF-8?q?refactor:=20Image=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapbefine/{common/entity => image/domain}/Image.java | 2 +- .../java/com/mapbefine/mapbefine/member/domain/MemberInfo.java | 2 +- .../main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java | 2 +- .../java/com/mapbefine/mapbefine/topic/domain/TopicInfo.java | 2 +- .../java/com/mapbefine/mapbefine/common/entity/ImageTest.java | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) rename backend/src/main/java/com/mapbefine/mapbefine/{common/entity => image/domain}/Image.java (95%) diff --git a/backend/src/main/java/com/mapbefine/mapbefine/common/entity/Image.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/Image.java similarity index 95% rename from backend/src/main/java/com/mapbefine/mapbefine/common/entity/Image.java rename to backend/src/main/java/com/mapbefine/mapbefine/image/domain/Image.java index ae0a810a..3a2b9239 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/common/entity/Image.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/Image.java @@ -1,4 +1,4 @@ -package com.mapbefine.mapbefine.common.entity; +package com.mapbefine.mapbefine.image.domain; import static lombok.AccessLevel.PROTECTED; diff --git a/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java b/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java index ee36d80a..fd89103b 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java @@ -6,7 +6,7 @@ import static com.mapbefine.mapbefine.member.exception.MemberErrorCode.ILLEGAL_NICKNAME_NULL; import static lombok.AccessLevel.PROTECTED; -import com.mapbefine.mapbefine.common.entity.Image; +import com.mapbefine.mapbefine.image.domain.Image; import com.mapbefine.mapbefine.common.util.RegexUtil; import com.mapbefine.mapbefine.member.exception.MemberException.MemberBadRequestException; import jakarta.persistence.Column; diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java index 4da68c7d..293357ae 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java @@ -1,7 +1,7 @@ package com.mapbefine.mapbefine.pin.domain; import com.mapbefine.mapbefine.common.entity.BaseTimeEntity; -import com.mapbefine.mapbefine.common.entity.Image; +import com.mapbefine.mapbefine.image.domain.Image; import jakarta.persistence.Column; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicInfo.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicInfo.java index f7dfe464..827dcb51 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicInfo.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicInfo.java @@ -6,7 +6,7 @@ import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.ILLEGAL_NAME_NULL; import static lombok.AccessLevel.PROTECTED; -import com.mapbefine.mapbefine.common.entity.Image; +import com.mapbefine.mapbefine.image.domain.Image; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicBadRequestException; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; diff --git a/backend/src/test/java/com/mapbefine/mapbefine/common/entity/ImageTest.java b/backend/src/test/java/com/mapbefine/mapbefine/common/entity/ImageTest.java index 8c6a3b6c..0453a705 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/common/entity/ImageTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/common/entity/ImageTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.mapbefine.mapbefine.common.exception.BadRequestException.ImageBadRequestException; +import com.mapbefine.mapbefine.image.domain.Image; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; From 8cf1a4dc57477cd22112345205fe1ab6a3edcead Mon Sep 17 00:00:00 2001 From: junpakPark Date: Sun, 1 Oct 2023 16:49:51 +0900 Subject: [PATCH 3/7] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index c4192d94..75ac1086 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -35,6 +35,9 @@ dependencies { implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359' + // S3 + implementation 'com.amazonaws:aws-java-sdk-s3:1.12.547' + annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' @@ -45,9 +48,6 @@ dependencies { testImplementation 'io.rest-assured:spring-mock-mvc' testImplementation 'org.assertj:assertj-core:3.19.0' - // S3 - implementation platform('com.amazonaws:aws-java-sdk-bom:1.11.1000') - implementation 'com.amazonaws:aws-java-sdk-s3' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' From 40d6589d14deef40a2037388d1f66cedf756bebd Mon Sep 17 00:00:00 2001 From: junpakPark Date: Sun, 1 Oct 2023 16:55:22 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor:=20ImageExtension=20=EB=B0=8F=20Im?= =?UTF-8?q?ageName=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/domain/ImageExtension.java | 29 ++++++----------- .../mapbefine/image/domain/ImageName.java | 32 ------------------- .../image/domain/ImageNameGenerator.java | 30 +++++++++++++++++ .../mapbefine/image/domain/UploadFile.java | 4 +-- .../mapbefine/image/ImageExtensionTest.java | 5 ++- 5 files changed, 44 insertions(+), 56 deletions(-) delete mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageName.java create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageNameGenerator.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageExtension.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageExtension.java index 90134906..b915b1dc 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageExtension.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageExtension.java @@ -3,32 +3,23 @@ import static com.mapbefine.mapbefine.image.exception.ImageErrorCode.ILLEGAL_IMAGE_FILE_EXTENSION; import com.mapbefine.mapbefine.image.exception.ImageException.ImageBadRequestException; -import java.util.Arrays; public enum ImageExtension { - JPEG(".jpeg"), - JPG(".jpg"), - JFIF(".jfif"), - PNG(".png"), - SVG(".svg"), + JPEG, JPG, JFIF, PNG, SVG, ; - private final String extension; + public static String extract(String fileName) { + int index = fileName.lastIndexOf(".") + 1; + String extension = fileName.substring(index); - ImageExtension(final String extension) { - this.extension = extension; - } - - public static ImageExtension from(String imageFileName) { - return Arrays.stream(values()) - .filter(imageExtension -> imageFileName.endsWith(imageExtension.getExtension())) - .findFirst() - .orElseThrow(() -> new ImageBadRequestException(ILLEGAL_IMAGE_FILE_EXTENSION)); - } + try { + ImageExtension imageExtension = valueOf(extension.toUpperCase()); - public String getExtension() { - return extension; + return imageExtension.name().toLowerCase(); + } catch (IllegalArgumentException e) { + throw new ImageBadRequestException(ILLEGAL_IMAGE_FILE_EXTENSION); + } } } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageName.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageName.java deleted file mode 100644 index 63a011a5..00000000 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageName.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mapbefine.mapbefine.image.domain; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -public class ImageName { - - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSSSS"); - - private final String fileName; - - private ImageName(String fileName) { - this.fileName = fileName; - } - - public static ImageName from(String originalFileName) { - String fileName = FORMATTER.format(LocalDateTime.now()); - String extension = extractExtension(originalFileName); - - return new ImageName(fileName + extension); - } - - private static String extractExtension(String originalFileName) { - return ImageExtension.from(originalFileName) - .getExtension(); - } - - public String getFileName() { - return fileName; - } - -} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageNameGenerator.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageNameGenerator.java new file mode 100644 index 00000000..b4c0a88a --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageNameGenerator.java @@ -0,0 +1,30 @@ +package com.mapbefine.mapbefine.image.domain; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +public class ImageNameGenerator { + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMddHHmmss"); + + private ImageNameGenerator() { + } + + public static String generate(String originalFileName) { + String fileName = generateUniqueName(); + String extension = ImageExtension.extract(originalFileName); + + return String.join(".", fileName, extension.toLowerCase()); + } + + private static String generateUniqueName() { + String dateFormat = DATE_TIME_FORMATTER.format(LocalDateTime.now()); + + String uuid = UUID.randomUUID().toString(); + String uniqueName = uuid.split("-")[0]; + + return String.join("_", dateFormat, uniqueName); + } + +} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java index b8199f2b..3f782cd9 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java @@ -24,10 +24,10 @@ private UploadFile( public static UploadFile from( MultipartFile multipartFile ) throws IOException { - ImageName imageName = ImageName.from(multipartFile.getOriginalFilename()); + String imageName = ImageNameGenerator.generate(multipartFile.getOriginalFilename()); byte[] multipartFileBytes = multipartFile.getBytes(); - return new UploadFile(imageName.getFileName(), multipartFileBytes); + return new UploadFile(imageName, multipartFileBytes); } @Override diff --git a/backend/src/test/java/com/mapbefine/mapbefine/image/ImageExtensionTest.java b/backend/src/test/java/com/mapbefine/mapbefine/image/ImageExtensionTest.java index 2f9ed9a0..ede60b9a 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/image/ImageExtensionTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/image/ImageExtensionTest.java @@ -16,8 +16,7 @@ class ImageExtensionTest { @ValueSource(strings = {"image.jpeg", "image.jpg", "image.jfif", "image.png", "image.svg"}) void createImageExtensionByFileName_Success(String fileName) { // given when - String extension = ImageExtension.from(fileName) - .getExtension(); + String extension = ImageExtension.extract(fileName); // then assertThat(fileName).contains(extension); @@ -28,7 +27,7 @@ void createImageExtensionByFileName_Success(String fileName) { @ValueSource(strings = {"image.pppng", "image.jpeeg", "image.gi"}) void createImageExtensionByFileName_Fail(String fileName) { // given when then - assertThatThrownBy(() -> ImageExtension.from(fileName)) + assertThatThrownBy(() -> ImageExtension.extract(fileName)) .isInstanceOf(ImageBadRequestException.class); } From f09978d87f2a342613e10ceb378c7d0074ab8eb2 Mon Sep 17 00:00:00 2001 From: junpakPark Date: Sun, 1 Oct 2023 20:20:13 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20ImageInternalServerException=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/exception/InternalServerException.java | 9 +++++++++ .../mapbefine/image/exception/ImageErrorCode.java | 3 ++- .../mapbefine/image/exception/ImageException.java | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/common/exception/InternalServerException.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/common/exception/InternalServerException.java b/backend/src/main/java/com/mapbefine/mapbefine/common/exception/InternalServerException.java new file mode 100644 index 00000000..19577640 --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/common/exception/InternalServerException.java @@ -0,0 +1,9 @@ +package com.mapbefine.mapbefine.common.exception; + +import org.springframework.http.HttpStatus; + +public class InternalServerException extends GlobalException { + public InternalServerException(ErrorCode errorCode) { + super(errorCode, HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageErrorCode.java b/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageErrorCode.java index c92fd970..4ff9b10d 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageErrorCode.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageErrorCode.java @@ -6,7 +6,8 @@ public enum ImageErrorCode { ILLEGAL_IMAGE_FILE_EXTENSION("10000", "지원하지 않는 이미지 파일입니다."), - IMAGE_FILE_IS_NULL("10001", "이미지가 선택되지 않았습니다.") + IMAGE_FILE_IS_NULL("10001", "이미지가 선택되지 않았습니다."), + UNKNOWN_SERVER_ERROR("10002", "이미지 업로드를 실패했습니다.") ; private final String code; diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageException.java b/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageException.java index 680140d6..afeae57e 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageException.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/exception/ImageException.java @@ -2,6 +2,7 @@ import com.mapbefine.mapbefine.common.exception.BadRequestException; import com.mapbefine.mapbefine.common.exception.ErrorCode; +import com.mapbefine.mapbefine.common.exception.InternalServerException; public class ImageException { @@ -13,4 +14,11 @@ public ImageBadRequestException(ImageErrorCode errorCode) { } + public static class ImageInternalServerException extends InternalServerException { + + public ImageInternalServerException(ImageErrorCode errorCode) { + super(new ErrorCode<>(errorCode.getCode(), errorCode.getMessage())); + } + } + } From 36f4616600d759e7bf854024a3f5da5ae717280d Mon Sep 17 00:00:00 2001 From: junpakPark Date: Sun, 1 Oct 2023 20:21:49 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20S3ImageUploader=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapbefine/image/domain/ImageUploader.java | 5 +- .../image/infrastructure/S3ImageUploader.java | 86 +++++++++++++++++++ .../image/infrastructure/S3Uploader.java | 55 ------------ 3 files changed, 88 insertions(+), 58 deletions(-) create mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3ImageUploader.java delete mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3Uploader.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java index 5ba84060..62784d3b 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/ImageUploader.java @@ -1,12 +1,11 @@ package com.mapbefine.mapbefine.image.domain; -import java.io.IOException; import org.springframework.web.multipart.MultipartFile; public interface ImageUploader { - void upload(MultipartFile multipartFile) throws IOException; + Image upload(MultipartFile multipartFile); - void delete(String key); + void delete(String imageName); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3ImageUploader.java b/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3ImageUploader.java new file mode 100644 index 00000000..e201f6c6 --- /dev/null +++ b/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3ImageUploader.java @@ -0,0 +1,86 @@ +package com.mapbefine.mapbefine.image.infrastructure; + +import static com.mapbefine.mapbefine.image.exception.ImageErrorCode.UNKNOWN_SERVER_ERROR; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.mapbefine.mapbefine.image.domain.Image; +import com.mapbefine.mapbefine.image.domain.ImageNameGenerator; +import com.mapbefine.mapbefine.image.domain.ImageUploader; +import com.mapbefine.mapbefine.image.exception.ImageException.ImageInternalServerException; +import java.io.IOException; +import java.io.InputStream; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +public class S3ImageUploader implements ImageUploader { + + @Value("${s3.bucket}") + private String bucket; + @Value("${s3.folder}") + private String folder; + @Value("${prefix.upload.path}") + private String prefixUploadPath; + private final AmazonS3 amazonS3; + + public S3ImageUploader(AmazonS3 amazonS3) { + this.amazonS3 = amazonS3; + } + + @Override + public Image upload(MultipartFile multipartFile) { + String imageName = ImageNameGenerator.generate(multipartFile.getContentType()); + ObjectMetadata metadata = getMetadata(multipartFile); + + try (InputStream inputStream = multipartFile.getInputStream()) { + PutObjectRequest request = createPutObjectRequest(imageName, inputStream, metadata); + amazonS3.putObject(request); + + return getImage(imageName); + } catch (IOException exception) { + throw new ImageInternalServerException(UNKNOWN_SERVER_ERROR); + } + } + + private PutObjectRequest createPutObjectRequest( + String imageName, + InputStream inputStream, + ObjectMetadata metadata + ) { + String key = folder + imageName; + + return new PutObjectRequest(bucket, key, inputStream, metadata); + } + + private ObjectMetadata getMetadata(MultipartFile multipartFile) { + ObjectMetadata metadata = new ObjectMetadata(); + + metadata.setContentType(multipartFile.getContentType()); + metadata.setContentLength(multipartFile.getSize()); + + return metadata; + } + + private Image getImage(String imageName) { + String imageUrl = String.join( + "/", + prefixUploadPath, + imageName + ); + + return Image.from(imageUrl); + } + + @Override + public void delete(String imageName) { + String key = folder + imageName; + DeleteObjectRequest request = new DeleteObjectRequest(bucket, key); + + amazonS3.deleteObject(request); + } + +} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3Uploader.java b/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3Uploader.java deleted file mode 100644 index 0f5784e8..00000000 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/infrastructure/S3Uploader.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.mapbefine.mapbefine.image.infrastructure; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.mapbefine.mapbefine.image.domain.ImageUploader; -import java.io.File; -import java.io.IOException; -import java.util.Objects; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -@Component -public class S3Uploader implements ImageUploader { - - @Value("${s3.bucket}") - private String bucket; - private final AmazonS3 amazonS3; - - public S3Uploader(AmazonS3 amazonS3) { - this.amazonS3 = amazonS3; - } - - @Override - public void upload(MultipartFile multipartFile) throws IOException { - File tempFile = null; - - try { - tempFile = File.createTempFile("upload_", ".tmp"); - multipartFile.transferTo(tempFile); - amazonS3.putObject(new PutObjectRequest( - bucket, - multipartFile.getOriginalFilename(), - tempFile - )); - } catch (IOException exception) { - throw new IOException(exception); - } finally { - removeTempFileIfExists(tempFile); - } - } - - private void removeTempFileIfExists(File tempFile) { - if (Objects.nonNull(tempFile) && tempFile.exists()) { - tempFile.delete(); - } - } - - @Override - public void delete(String key) { - amazonS3.deleteObject(new DeleteObjectRequest(bucket, key)); - } - -} From 9c307f9adf32223c84c3b7a095971fecf1e035e3 Mon Sep 17 00:00:00 2001 From: junpakPark Date: Sun, 1 Oct 2023 21:14:09 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refactor:=20ImageService=20=EB=B0=8F=20Uplo?= =?UTF-8?q?adFile=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/application/ImageService.java | 11 --- .../image/application/S3ImageService.java | 43 ---------- .../mapbefine/image/domain/UploadFile.java | 81 ------------------- .../mapbefine/image/TestImageService.java | 17 ---- 4 files changed, 152 deletions(-) delete mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/application/ImageService.java delete mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java delete mode 100644 backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java delete mode 100644 backend/src/test/java/com/mapbefine/mapbefine/image/TestImageService.java diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/application/ImageService.java b/backend/src/main/java/com/mapbefine/mapbefine/image/application/ImageService.java deleted file mode 100644 index 3a9d0782..00000000 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/application/ImageService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mapbefine.mapbefine.image.application; - -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -@Service -public interface ImageService { - - String upload(MultipartFile multipartFile); - -} diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java b/backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java deleted file mode 100644 index 6bf7cfb1..00000000 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/application/S3ImageService.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.mapbefine.mapbefine.image.application; - -import com.mapbefine.mapbefine.image.domain.ImageUploader; -import com.mapbefine.mapbefine.image.domain.UploadFile; -import java.io.IOException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -@Service -@Profile("!test") -public class S3ImageService implements ImageService { - - @Value("${prefix.upload.path}") - private String prefixUploadPath; - private final ImageUploader imageUploader; - - public S3ImageService(ImageUploader imageUploader) { - this.imageUploader = imageUploader; - } - - @Override - public String upload(MultipartFile multipartFile) { - try { - UploadFile uploadFile = UploadFile.from(multipartFile); - imageUploader.upload(uploadFile); - return getUploadPath(uploadFile); - } catch (IOException exception) { - throw new RuntimeException(exception); - } - } - - private String getUploadPath(UploadFile uploadFile) { - return String.join( - "/", - prefixUploadPath, - uploadFile.getOriginalFilename() - ); - } - -} - diff --git a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java b/backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java deleted file mode 100644 index 3f782cd9..00000000 --- a/backend/src/main/java/com/mapbefine/mapbefine/image/domain/UploadFile.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.mapbefine.mapbefine.image.domain; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import org.springframework.core.io.Resource; -import org.springframework.web.multipart.MultipartFile; - -public class UploadFile implements MultipartFile { - - private final String fileName; - private final byte[] bytes; - - private UploadFile( - String fileName, - byte[] bytes - ) { - this.fileName = fileName; - this.bytes = bytes; - } - - public static UploadFile from( - MultipartFile multipartFile - ) throws IOException { - String imageName = ImageNameGenerator.generate(multipartFile.getOriginalFilename()); - byte[] multipartFileBytes = multipartFile.getBytes(); - - return new UploadFile(imageName, multipartFileBytes); - } - - @Override - public String getName() { - return fileName; - } - - @Override - public String getOriginalFilename() { - return fileName; - } - - @Override - public String getContentType() { - return null; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public long getSize() { - return 0; - } - - @Override - public byte[] getBytes() { - return bytes; - } - - @Override - public InputStream getInputStream() { - return new ByteArrayInputStream(bytes); - } - - @Override - public Resource getResource() { - return MultipartFile.super - .getResource(); - } - - @Override - public void transferTo(File dest) throws IOException, IllegalStateException { - try (FileOutputStream fileOutputStream = new FileOutputStream(dest)) { - fileOutputStream.write(bytes); - } - } - -} diff --git a/backend/src/test/java/com/mapbefine/mapbefine/image/TestImageService.java b/backend/src/test/java/com/mapbefine/mapbefine/image/TestImageService.java deleted file mode 100644 index dd33ef61..00000000 --- a/backend/src/test/java/com/mapbefine/mapbefine/image/TestImageService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.mapbefine.mapbefine.image; - -import com.mapbefine.mapbefine.image.application.ImageService; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -@Service -@Profile("test") -public class TestImageService implements ImageService { - - @Override - public String upload(MultipartFile multipartFile) { - return "https://map-befine-official.github.io/favicon.png"; - } - -}