Skip to content

Commit

Permalink
Merge pull request #150 from IT-Cotato/develop
Browse files Browse the repository at this point in the history
[Release] V2 2024.09.12.01
  • Loading branch information
Youthhing authored Sep 12, 2024
2 parents 45a7625 + 382fb42 commit 63664a8
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 37 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public record AddSessionImageRequest(
@NotNull
Long sessionId,
@NotNull
MultipartFile image
MultipartFile image,
@NotNull
Integer order
) {
}
9 changes: 5 additions & 4 deletions src/main/java/org/cotato/csquiz/common/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public enum ErrorCode {
EDUCATION_STATUS_NOT_BEFORE(HttpStatus.BAD_REQUEST, "E-402", "이미 시작한 적이 있는 교육입니다."),
MEMBER_CANT_ACCESS(HttpStatus.BAD_REQUEST, "E-403", "해당 멤버의 ROLE로 접근할 수 없습니다"),

//세션 사진
SESSION_IMAGE_COUNT_MISMATCH(HttpStatus.BAD_REQUEST, "P-101", "저장된 사진 수와 요청 사진 수가 다릅니다."),
SESSION_ORDER_INVALID(HttpStatus.BAD_REQUEST, "P-102", "입력한 순서는 유효하지 않습니다."),

FILE_EXTENSION_FAULT(HttpStatus.BAD_REQUEST, "F-001", "해당 파일 확장자 명이 존재하지 않습니다."),
FILE_IS_EMPTY(HttpStatus.BAD_REQUEST, "F-002", "파일이 비어있습니다"),

//세션 사진
SESSION_IMAGE_COUNT_MISMATCH(HttpStatus.BAD_REQUEST, "IM-101", "저장된 사진 수와 요청 사진 수가 다릅니다."),
SESSION_ORDER_INVALID(HttpStatus.BAD_REQUEST, "IM-102", "입력한 순서는 유효하지 않습니다."),

INVALID_ANSWER(HttpStatus.BAD_REQUEST, "Q-101", "객관식 문제는 숫자 형식의 값만 정답으로 추가할 수 있습니다."),
CONTENT_IS_NOT_ANSWER(HttpStatus.BAD_REQUEST, "Q-201", "추가되지 않은 정답을 추가할 수 없습니다."),
QUIZ_NUMBER_DUPLICATED(HttpStatus.CONFLICT, "Q-301", "퀴즈 번호는 중복될 수 없습니다."),
Expand Down Expand Up @@ -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;
Expand Down
33 changes: 9 additions & 24 deletions src/main/java/org/cotato/csquiz/common/s3/S3Uploader.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
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;
import com.amazonaws.services.s3.model.CannedAccessControlList;
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;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@Slf4j
@RequiredArgsConstructor
Expand All @@ -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();
}
Expand Down Expand Up @@ -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);
}
}
}
36 changes: 36 additions & 0 deletions src/main/java/org/cotato/csquiz/common/util/FileUtil.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.cotato.csquiz.domain.generation.repository;

import java.util.List;
import java.util.Optional;
import org.cotato.csquiz.domain.generation.entity.Session;
import org.cotato.csquiz.domain.generation.entity.SessionImage;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -11,5 +10,5 @@ public interface SessionImageRepository extends JpaRepository<SessionImage, Long

List<SessionImage> findAllBySessionIn(List<Session> sessions);

Optional<SessionImage> findFirstBySessionOrderByOrderDesc(Session session);
boolean existsBySessionAndOrder(Session session, Integer order);
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<ProjectImage> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ public AddSessionImageResponse additionalSessionImage(AddSessionImageRequest req

S3Info imageInfo = s3Uploader.uploadFiles(request.image(), SESSION_BUCKET_DIRECTORY);

Integer imageOrder = sessionImageRepository.findFirstBySessionOrderByOrderDesc(session)
.map(sessionImage -> sessionImage.getOrder() + 1).orElse(0);
if (sessionImageRepository.existsBySessionAndOrder(session, request.order())) {
throw new AppException(ErrorCode.SESSION_ORDER_INVALID);
}

SessionImage sessionImage = SessionImage.builder()
.session(session)
.s3Info(imageInfo)
.order(imageOrder)
.order(request.order())
.build();

return AddSessionImageResponse.from(sessionImageRepository.save(sessionImage));
Expand Down

0 comments on commit 63664a8

Please sign in to comment.