diff --git a/src/main/java/com/uhdyl/backend/image/controller/ImageController.java b/src/main/java/com/uhdyl/backend/image/controller/ImageController.java index a460748..1ccb909 100644 --- a/src/main/java/com/uhdyl/backend/image/controller/ImageController.java +++ b/src/main/java/com/uhdyl/backend/image/controller/ImageController.java @@ -35,8 +35,9 @@ public ResponseEntity> uploadProfileImag Long userId, @RequestParam MultipartFile image ){ + MultipartFile[] images = {image}; return ResponseEntity.ok(createSuccessResponse( - imageService.uploadImage(image, "profile/" + userId + "/") + imageService.uploadImage(images, "profile/" + userId + "/").get(0) )); } @@ -49,8 +50,9 @@ public ResponseEntity> uploadChatMessage @RequestParam Long roomId, @RequestParam MultipartFile image ){ + MultipartFile[] images = {image}; return ResponseEntity.ok(createSuccessResponse( - imageService.uploadImage(image, "chat/" + roomId + "/") + imageService.uploadImage(images, "chat/" + roomId + "/").get(0) )); } @@ -62,15 +64,9 @@ public ResponseEntity> uploadChatMessage public ResponseEntity>> uploadProductImage( @RequestParam List images ){ - - List response = new ArrayList<>(); - for(int i = 0; i < images.size(); i++){ - response.add( - imageService.uploadImage(images.get(i), "product/" + (i + 1) + "/") - ); - } - - return ResponseEntity.ok(createSuccessResponse(response)); + MultipartFile[] imageArray = new MultipartFile[images.size()]; + images.toArray(imageArray); + return ResponseEntity.ok(createSuccessResponse(imageService.uploadImage(imageArray, "product/" + 1 + "/"))); } @PostMapping("/image/review") @@ -80,8 +76,9 @@ public ResponseEntity> uploadReviewImage Long userId, @RequestParam MultipartFile image ){ + MultipartFile[] images = {image}; return ResponseEntity.ok(createSuccessResponse( - imageService.uploadImage(image, "review/" + userId + "/") + imageService.uploadImage(images, "review/" + userId + "/").get(0) )); } /** diff --git a/src/main/java/com/uhdyl/backend/image/service/ImageService.java b/src/main/java/com/uhdyl/backend/image/service/ImageService.java index 546d87c..601749f 100644 --- a/src/main/java/com/uhdyl/backend/image/service/ImageService.java +++ b/src/main/java/com/uhdyl/backend/image/service/ImageService.java @@ -12,23 +12,22 @@ import com.uhdyl.backend.image.dto.response.ImagePublicIdResponse; import com.uhdyl.backend.image.dto.response.ImageSavedSuccessResponse; import com.uhdyl.backend.image.repository.ImageRepository; -import com.uhdyl.backend.product.domain.Product; import com.uhdyl.backend.product.repository.ProductRepository; import com.uhdyl.backend.review.domain.Review; import com.uhdyl.backend.review.repository.ReviewRepository; import com.uhdyl.backend.user.domain.User; import com.uhdyl.backend.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; @Service +@Slf4j @RequiredArgsConstructor @Transactional(readOnly = true) public class ImageService { @@ -40,30 +39,53 @@ public class ImageService { private final ReviewRepository reviewRepository; private final ProductRepository productRepository; - public ImageSavedSuccessResponse uploadImage(MultipartFile image, String folderPath){ + private static final Set ALLOWED_CONTENT_TYPES = + Set.of("image/jpeg", "image/png", "image/webp", "image/gif"); - if(image == null || image.isEmpty()) - throw new BusinessException(ExceptionType.INVALID_IMAGE_FILE); + public List uploadImage(MultipartFile[] images, String folderPath){ - if(image.getSize() > 1024 * 1024 * 5) - throw new BusinessException(ExceptionType.IMAGE_SIZE_EXCEEDED); + List uploadedImagesPublicId = new ArrayList<>(); + List responses = new ArrayList<>(); try { - Map result = cloudinary.uploader().upload( - image.getBytes(), - Map.of( - "folder", folderPath, - "public_id", UUID.randomUUID().toString() - ) - ); - - if(result.get("secure_url") == null || result.get("public_id") == null) - throw new BusinessException(ExceptionType.IMAGE_UPLOAD_FAILED); - - return ImageSavedSuccessResponse.to(result.get("secure_url").toString(), result.get("public_id").toString()); - } catch (IOException e) { + for (MultipartFile image : images) { + + if (image == null || image.isEmpty()) + throw new BusinessException(ExceptionType.INVALID_IMAGE_FILE); + + if (image.getSize() > 1024 * 1024 * 5) + throw new BusinessException(ExceptionType.IMAGE_SIZE_EXCEEDED); + + String contentType = image.getContentType(); + if (!ALLOWED_CONTENT_TYPES.contains(contentType)) + throw new BusinessException(ExceptionType.INVALID_IMAGE_FILE); + + Map result = cloudinary.uploader().upload( + image.getBytes(), + Map.of( + "folder", folderPath, + "public_id", UUID.randomUUID().toString() + ) + ); + + + if (result.get("secure_url") == null || result.get("public_id") == null) + throw new BusinessException(ExceptionType.IMAGE_UPLOAD_FAILED); + + uploadedImagesPublicId.add(result.get("public_id").toString()); + responses.add(ImageSavedSuccessResponse.to(result.get("secure_url").toString(), result.get("public_id").toString())); + + } + } + catch (BusinessException e) { + rollbackImage(uploadedImagesPublicId); + throw e; + } + catch (Exception e) { + rollbackImage(uploadedImagesPublicId); throw new BusinessException(ExceptionType.IMAGE_UPLOAD_FAILED); } + return responses; } @Transactional @@ -147,4 +169,19 @@ public ImagePublicIdResponse getPublicId(Long userId, String imageUrl, ImageType } return ImagePublicIdResponse.to("이미지가 존재하지 않습니다."); } + + public void rollbackImage(List publicIds){ + try{ + for (String publicId : publicIds) { + try { + cloudinary.uploader().destroy(publicId, ObjectUtils.emptyMap()); + } catch (IOException e) { + log.warn("이미지 롤백에 실패했습니다. publicId : {}", publicId); + } + } + } + catch (Exception e){ + log.warn("이미지 롤백이 일부 실패했습니다."); + } + } }