diff --git a/build.gradle b/build.gradle index 7cd5ffdc..1cab0c52 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,10 @@ dependencies { //S3 관련 의존성 부여 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + //webp 변환 라이브러리 + implementation "com.sksamuel.scrimage:scrimage-core:4.0.32" + implementation "com.sksamuel.scrimage:scrimage-webp:4.0.32" + implementation group: 'org.redisson', name: 'redisson-spring-boot-starter', version: '3.32.0' implementation 'io.jsonwebtoken:jjwt:0.9.1' diff --git a/src/main/java/org/cotato/csquiz/common/error/ErrorCode.java b/src/main/java/org/cotato/csquiz/common/error/ErrorCode.java index 839a257f..7d3a27b6 100644 --- a/src/main/java/org/cotato/csquiz/common/error/ErrorCode.java +++ b/src/main/java/org/cotato/csquiz/common/error/ErrorCode.java @@ -93,6 +93,7 @@ public enum ErrorCode { SCORER_LOCK_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "S-006", "득점자 락 획득 과정에서 에러 발생"), IMAGE_CONVERT_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "S-007", "로컬 이미지 변환에 실패했습니다"), SSE_SEND_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "S-008", "서버 이벤트 전송간 오류 발생"), + WEBP_CONVERT_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "S-009", "webp 변환에 실패했습니다") ; private final HttpStatus httpStatus; diff --git a/src/main/java/org/cotato/csquiz/common/s3/S3Uploader.java b/src/main/java/org/cotato/csquiz/common/s3/S3Uploader.java index 117f94f7..75db8840 100644 --- a/src/main/java/org/cotato/csquiz/common/s3/S3Uploader.java +++ b/src/main/java/org/cotato/csquiz/common/s3/S3Uploader.java @@ -1,6 +1,6 @@ package org.cotato.csquiz.common.s3; -import static org.cotato.csquiz.common.util.FileUtil.extractFileExtension; +import static org.cotato.csquiz.common.util.FileUtil.convert; import static org.cotato.csquiz.common.util.FileUtil.isImageFileExtension; import com.amazonaws.services.s3.AmazonS3Client; @@ -8,9 +8,7 @@ import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; import org.cotato.csquiz.common.entity.S3Info; -import org.cotato.csquiz.common.error.ErrorCode; import org.cotato.csquiz.common.error.exception.ImageException; -import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -18,8 +16,6 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; @Slf4j @RequiredArgsConstructor @@ -36,13 +32,17 @@ public S3Info uploadFiles(MultipartFile multipartFile, String folderName) throws log.info("{} 사진 업로드", multipartFile.getOriginalFilename()); File localUploadFile = convert(multipartFile); - String fileName = folderName + "/" + localUploadFile.getName(); - String uploadUrl = putS3(localUploadFile, fileName); - localUploadFile.delete(); + return uploadFiles(localUploadFile, folderName); + } + + public S3Info uploadFiles(File file, String folderName) { + String fileName = folderName + "/" + file.getName(); + String uploadUrl = putS3(file, fileName); + file.delete(); return S3Info.builder() .folderName(folderName) - .fileName(localUploadFile.getName()) + .fileName(file.getName()) .url(uploadUrl) .build(); } @@ -74,19 +74,4 @@ private boolean isImageFile(File file) { return isImageFileExtension(extension); } - - private File convert(MultipartFile file) throws ImageException { - String fileExtension = extractFileExtension(file); - File convertFile = new File(System.getProperty("user.dir") + "/" + UUID.randomUUID() + "." + fileExtension); - - try { - FileOutputStream fos = new FileOutputStream(convertFile); - fos.write(file.getBytes()); - fos.close(); - - return convertFile; - } catch (IOException e) { - throw new ImageException(ErrorCode.IMAGE_CONVERT_FAIL); - } - } } diff --git a/src/main/java/org/cotato/csquiz/common/util/FileUtil.java b/src/main/java/org/cotato/csquiz/common/util/FileUtil.java index efed8113..40267f1f 100644 --- a/src/main/java/org/cotato/csquiz/common/util/FileUtil.java +++ b/src/main/java/org/cotato/csquiz/common/util/FileUtil.java @@ -1,6 +1,12 @@ package org.cotato.csquiz.common.util; +import com.sksamuel.scrimage.ImmutableImage; +import com.sksamuel.scrimage.webp.WebpWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.List; +import java.util.UUID; import org.cotato.csquiz.common.error.ErrorCode; import org.cotato.csquiz.common.error.exception.ImageException; import org.springframework.web.multipart.MultipartFile; @@ -21,4 +27,34 @@ public static String extractFileExtension(MultipartFile file) throws ImageExcept public static boolean isImageFileExtension(String fileExtension) { return IMAGE_FILE_EXTENSIONS.contains(fileExtension.toLowerCase()); } + + public static File convert(MultipartFile file) throws ImageException { + String fileExtension = extractFileExtension(file); + File convertFile = new File(System.getProperty("user.dir") + "/" + UUID.randomUUID() + "." + fileExtension); + + try { + FileOutputStream fos = new FileOutputStream(convertFile); + fos.write(file.getBytes()); + fos.close(); + + return convertFile; + } catch (IOException e) { + throw new ImageException(ErrorCode.IMAGE_CONVERT_FAIL); + } + } + + public static File convertToWebp(File originalFile) throws ImageException { + try { + String fileNameWithoutExtension = originalFile.getName().replaceFirst("[.][^.]+$", ""); + + File webpFile = ImmutableImage.loader() + .fromFile(originalFile) + .output(WebpWriter.DEFAULT, new File(fileNameWithoutExtension + ".webp")); + + originalFile.delete(); + return webpFile; + } catch (IOException e) { + throw new ImageException(ErrorCode.WEBP_CONVERT_FAIL); + } + } } diff --git a/src/main/java/org/cotato/csquiz/domain/generation/service/ProjectImageService.java b/src/main/java/org/cotato/csquiz/domain/generation/service/ProjectImageService.java index 2921671c..622f6683 100644 --- a/src/main/java/org/cotato/csquiz/domain/generation/service/ProjectImageService.java +++ b/src/main/java/org/cotato/csquiz/domain/generation/service/ProjectImageService.java @@ -1,5 +1,8 @@ package org.cotato.csquiz.domain.generation.service; +import static org.cotato.csquiz.common.util.FileUtil.*; + +import java.io.File; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; @@ -21,14 +24,16 @@ public class ProjectImageService { private final ProjectImageRepository projectImageRepository; @Transactional - public void createProjectImage(Long projectId, MultipartFile logoImage, MultipartFile thumbNameImage) + public void createProjectImage(Long projectId, MultipartFile logoImage, MultipartFile thumbNailImage) throws ImageException { List newImages = new ArrayList<>(); - S3Info logoImageInfo = s3Uploader.uploadFiles(logoImage, PROJECT_IMAGE); + File webpLogoImage = convertToWebp(convert(logoImage)); + S3Info logoImageInfo = s3Uploader.uploadFiles(webpLogoImage, PROJECT_IMAGE); newImages.add(ProjectImage.logoImage(logoImageInfo, projectId)); - S3Info thumbNailInfo = s3Uploader.uploadFiles(thumbNameImage, PROJECT_IMAGE); + File webpThumbNailImage = convertToWebp(convert(thumbNailImage)); + S3Info thumbNailInfo = s3Uploader.uploadFiles(webpThumbNailImage, PROJECT_IMAGE); newImages.add(ProjectImage.thumbNailImage(thumbNailInfo, projectId)); projectImageRepository.saveAll(newImages);