From 0793997125265d9ced6d02b3f269b9fbe6fb6eed Mon Sep 17 00:00:00 2001 From: LimdaeIl Date: Tue, 9 Dec 2025 20:45:41 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8feat:=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=A3=A8=ED=8A=B8=EC=97=90=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=98=A4=EB=B2=84=EB=A1=9C=EB=94=A9[#44]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ImageUploadService.java | 61 ++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java b/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java index 36e8258..484fdd6 100644 --- a/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java +++ b/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java @@ -52,7 +52,8 @@ public ImageFile uploadOriginal(String dir, MultipartFile file, int index) { validateExtension(file.getOriginalFilename()); String originalFilename = file.getOriginalFilename(); - String key = buildKey(dir, originalFilename, index); +// String key = buildKey(dir, originalFilename, index); + String key = buildKey(originalFilename, index); byte[] bytes = resizeIfNeededKeepFormat(file); @@ -128,6 +129,39 @@ public List uploadAsWebpWithSizes( return result; } + public List uploadAsWebpWithSizes( + MultipartFile file, + int index, + List widths, + List heights + ) { + if (widths.size() != heights.size()) { + // 해당 예외는 이미지 예외 처리로 수행하지 않는다. + throw new IllegalArgumentException("widths와 heights의 길이가 일치해야 합니다."); + } + + validateImageSize(file); + validateImageContentType(file); + validateExtension(file.getOriginalFilename()); + + String baseName = buildBaseName(index); + List result = new ArrayList<>(); + + for (int i = 0; i < widths.size(); i++) { + ImageSize size = new ImageSize(widths.get(i), heights.get(i)); + + String key = baseName + "_" + size.width() + "x" + size.height() + ".webp"; + byte[] bytes = convertToWebpWithSize(file, size); + + putToS3(key, bytes, "image/webp"); + String url = awsS3Properties.getPublicEndpoint() + "/" + key; + + result.add(new ImageFile(key, url)); + } + + return result; + } + public void delete(String key) { s3Client.deleteObject(builder -> builder .bucket(awsS3Properties.getBucket()) @@ -197,9 +231,14 @@ private String extractExtension(String originalFilename) { } private void validateDir(String dir) { - if (dir == null || dir.isBlank()) { - throw new ImageException(ImageExceptionCode.DIR_REQUIRED); - } + // TODO: 모임 경로: FE 요청으로 루트 디렉토리로 개선했습니다. +// if (dir == null || dir.isBlank()) { +// throw new ImageException(ImageExceptionCode.DIR_REQUIRED); +// } + +// if (!dir.matches("[a-zA-Z0-9_\\-/]+")) { +// throw new ImageException(ImageExceptionCode.DIR_INVALID_PATTERN); +// } if (dir.contains("..") || dir.startsWith("/")) { throw new ImageException(ImageExceptionCode.DIR_INVALID_TRAVERSAL); @@ -208,10 +247,6 @@ private void validateDir(String dir) { if (dir.endsWith("/")) { throw new ImageException(ImageExceptionCode.DIR_TRAILING_SLASH); } - - if (!dir.matches("[a-zA-Z0-9_\\-/]+")) { - throw new ImageException(ImageExceptionCode.DIR_INVALID_PATTERN); - } } private String buildKey(String dir, String originalFilename, int index) { @@ -223,6 +258,16 @@ private String buildKey(String dir, String originalFilename, int index) { return dir + "/" + timestamp + "_" + index + "_" + uuid + extension; } + private String buildKey(String originalFilename, int index) { + String extension = extractExtension(originalFilename); + String timestamp = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + String uuid = UUID.randomUUID().toString(); + + return timestamp + "_" + index + "_" + uuid + extension; + } + + private String buildBaseName(int index) { String timestamp = LocalDateTime.now() .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); From aa786dd41931d6e2be7ae1fb0f7cea504c389085 Mon Sep 17 00:00:00 2001 From: LimdaeIl Date: Tue, 9 Dec 2025 20:48:12 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E2=9C=A8feat:=20=EB=8B=A8=20=EA=B1=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A3=A8=ED=8A=B8=EC=97=90=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=98=A4=EB=B2=84=EB=A1=9C=EB=94=A9[#44]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ImageUploadService.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java b/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java index 484fdd6..950e9bc 100644 --- a/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java +++ b/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java @@ -94,6 +94,26 @@ public ImageFile uploadAsWebpWithSize( return new ImageFile(key, url); } + public ImageFile uploadAsWebpWithSize( + MultipartFile file, + int index, + ImageSize size + ) { + validateImageSize(file); + validateImageContentType(file); + validateExtension(file.getOriginalFilename()); + + String baseName = buildBaseName(index); + String key = baseName + "_" + size.width() + "x" + size.height() + ".webp"; + + byte[] bytes = convertToWebpWithSize(file, size); + + putToS3(key, bytes, "image/webp"); + String url = awsS3Properties.getPublicEndpoint() + "/" + key; + + return new ImageFile(key, url); + } + public List uploadAsWebpWithSizes( String dir, MultipartFile file, From 4f0a3bc0e124c83162e1284a37d91c644e96d6ac Mon Sep 17 00:00:00 2001 From: LimdaeIl Date: Tue, 9 Dec 2025 20:55:26 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=A8feat:=20=EB=8B=A8=20=EA=B1=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A3=A8=ED=8A=B8=EC=97=90=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=98=A4=EB=B2=84=EB=A1=9C=EB=94=A9[#44]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/ImageUploadService.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java b/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java index 950e9bc..8b0932d 100644 --- a/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java +++ b/src/main/java/team/wego/wegobackend/image/application/service/ImageUploadService.java @@ -94,16 +94,19 @@ public ImageFile uploadAsWebpWithSize( return new ImageFile(key, url); } + + // TODO: 회원에서 사용할 단 건 이미지 저장 기능 public ImageFile uploadAsWebpWithSize( MultipartFile file, - int index, - ImageSize size + Integer width, + Integer height ) { validateImageSize(file); validateImageContentType(file); validateExtension(file.getOriginalFilename()); + ImageSize size = new ImageSize(width, height); - String baseName = buildBaseName(index); + String baseName = buildBaseName(); String key = baseName + "_" + size.width() + "x" + size.height() + ".webp"; byte[] bytes = convertToWebpWithSize(file, size); @@ -295,6 +298,13 @@ private String buildBaseName(int index) { return timestamp + "_" + index + "_" + uuid; } + private String buildBaseName() { + String timestamp = LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + String uuid = UUID.randomUUID().toString(); + return timestamp + "_" + uuid; + } + private byte[] resizeIfNeededKeepFormat(MultipartFile file) { return resizeToBox( file, From d8824e5fd546f2f358f0f708c50c2af33f376b7b Mon Sep 17 00:00:00 2001 From: LimdaeIl Date: Tue, 9 Dec 2025 20:55:56 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=94=A5remove:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=9E=84=ED=8F=AC=ED=8A=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0[#44]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wego/wegobackend/image/presentation/ImageController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/team/wego/wegobackend/image/presentation/ImageController.java b/src/main/java/team/wego/wegobackend/image/presentation/ImageController.java index 9d36396..33d4d41 100644 --- a/src/main/java/team/wego/wegobackend/image/presentation/ImageController.java +++ b/src/main/java/team/wego/wegobackend/image/presentation/ImageController.java @@ -1,6 +1,5 @@ package team.wego.wegobackend.image.presentation; -import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; From 8d76a1849141d6c5c24799598028d620f17035b0 Mon Sep 17 00:00:00 2001 From: LimdaeIl Date: Tue, 9 Dec 2025 20:56:56 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=E2=99=BB=EF=B8=8Frefactor:=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=AC?= =?UTF-8?q?=EC=88=98=EC=A0=95=20.http[#44]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/http/group/create.http | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/http/group/create.http b/src/test/http/group/create.http index 2a423c7..290fa65 100644 --- a/src/test/http/group/create.http +++ b/src/test/http/group/create.http @@ -140,22 +140,22 @@ POST http://localhost:8080/api/v1/groups/images/{{groupId}}/upload ?userId=1 Content-Type: multipart/form-data; boundary=boundary ---boundary +--boundary-- Content-Disposition: form-data; name="images"; filename="test-webp1.webp" Content-Type: image/webp < ../image/resources/test-webp1.webp ---boundary +--boundary-- Content-Disposition: form-data; name="images"; filename="test-webp1.webp" Content-Type: image/webp < ../image/resources/test-webp1.webp ---boundary +--boundary-- Content-Disposition: form-data; name="images"; filename="img1.png" Content-Type: image/png < ../image/resources/img1.png ---boundary +--boundary-- Content-Disposition: form-data; name="images"; filename="img2.jpg" Content-Type: image/jpeg