Skip to content

Commit

Permalink
Refactor: 사진 업로드 메타데이터 지정 및 파일 처리 코드 개선 (#96)
Browse files Browse the repository at this point in the history
* refactor: S3에 사진 넣을 때 S3에서 지정한 확장자명이 아닐 때 예외 발생하게 수정

* feat: 이미지 업로드 시 메타데이터 지정 작업 추가

* refactor: 파일 업로드 S3Uploader 로그 단순화 및 파일 처리 개선

- 가독성을 위해 불필요한 Optional 사용 제거
- 로그 메시지의 단순화
- 가독성 개선을 위한 코드 통합

* refactor: 이미지 파일 업로드 시 메타 데이터 추가 로직 간소화

* refactor: 파일 업로드 로직 간소화

* refactor: 이미지 확장자 목록 상수를 배열에서 리스트로 변경

* refactor: S3 예외 처리 로직 글로벌 예외처리로 분리
  • Loading branch information
gikhoon authored Aug 16, 2024
1 parent be377fb commit aa4a99c
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 51 deletions.
78 changes: 33 additions & 45 deletions src/main/java/org/cotato/csquiz/common/S3/S3Uploader.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.cotato.csquiz.common.S3;

import static org.cotato.csquiz.common.util.FileUtil.checkAllowedImageFileExtension;
import static org.cotato.csquiz.common.util.FileUtil.extractFileExtension;
import static org.cotato.csquiz.common.util.FileUtil.isImageFileExtension;

import com.amazonaws.SdkClientException;
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;
Expand All @@ -20,85 +20,73 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
@Component
public class S3Uploader {

private static final String CONTENT_TYPE = "multipart/formed-data";
private final AmazonS3Client amazonS3;

@Value("${cloud.aws.s3.bucket}")
private String bucket;

public S3Info uploadFiles(MultipartFile multipartFile, String folderName) throws ImageException {
log.info("upload Files {}", multipartFile);
File uploadFile = convert(multipartFile)
.orElseThrow(() -> new ImageException(ErrorCode.IMAGE_PROCESSING_FAIL));
String uploadUrl = upload(uploadFile, folderName);
log.info("{} 사진 업로드", multipartFile.getOriginalFilename());
File localUploadFile = convert(multipartFile);

String fileName = folderName + "/" + localUploadFile.getName();
String uploadUrl = putS3(localUploadFile, fileName);
localUploadFile.delete();

return S3Info.builder()
.folderName(folderName)
.fileName(uploadFile.getName())
.fileName(localUploadFile.getName())
.url(uploadUrl)
.build();
}

public void deleteFile(S3Info s3Info) {
String fileName = s3Info.getFolderName() + "/" + s3Info.getFileName();

log.info("deleteFile fileName = {}", fileName);
try {
amazonS3.deleteObject(bucket, fileName);
} catch (SdkClientException e) {
log.error("Failed to delete file: {}", s3Info.getUrl(), e);
}
log.info("{} 사진 삭제", fileName);
amazonS3.deleteObject(bucket, fileName);
}

private String upload(File uploadFile, String dirName) {
String fileName = dirName + "/" + uploadFile.getName();
String uploadUrl = putS3(uploadFile, fileName);
removeNewFile(uploadFile);
log.info(uploadUrl);
return uploadUrl;
}
private String putS3(File uploadFile, String fileName) {
PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, fileName, uploadFile)
.withCannedAcl(CannedAccessControlList.PublicRead);

private void removeNewFile(File targetFile) {
if (targetFile.delete()) {
log.info("삭제 완료");
} else {
log.error("삭제 에러");
if (isImageFile(uploadFile)) {
ObjectMetadata objMeta = new ObjectMetadata();
objMeta.setContentType(CONTENT_TYPE);
}
}

private String putS3(File uploadFile, String fileName) {
amazonS3.putObject(
new PutObjectRequest(bucket, fileName, uploadFile).withCannedAcl(CannedAccessControlList.PublicRead));
amazonS3.putObject(putObjectRequest);

return amazonS3.getUrl(bucket, fileName).toString();
}

private Optional<File> convert(MultipartFile file) throws ImageException {
String fileExtension = extractFileExtension(file);
checkAllowedImageFileExtension(fileExtension);
private boolean isImageFile(File file) {
String fileName = file.getName();
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);

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);
log.info("converted file name: {}", convertFile.getName());

try {
log.info("convert try start");
if (convertFile.createNewFile()) { // 바로 위에서 지정한 경로에 File이 생성됨 (경로가 잘못되었다면 생성 불가능)
FileOutputStream fos = new FileOutputStream(convertFile); // FileOutputStream 데이터를 파일에 바이트 스트림으로 저장하기 위함
fos.write(file.getBytes());
fos.close();
log.info("convert to " + convertFile);
return Optional.of(convertFile);
}
FileOutputStream fos = new FileOutputStream(convertFile);
fos.write(file.getBytes());
fos.close();

return convertFile;
} catch (IOException e) {
log.error("convert 실패", e);
throw new ImageException(ErrorCode.IMAGE_PROCESSING_FAIL);
}
log.info("convert empty");
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.cotato.csquiz.common.error.handler;

import com.amazonaws.services.s3.model.AmazonS3Exception;
import org.cotato.csquiz.common.error.exception.AppException;
import org.cotato.csquiz.common.error.exception.ImageException;
import org.cotato.csquiz.common.error.response.ErrorResponse;
Expand Down Expand Up @@ -84,4 +85,13 @@ public ResponseEntity<ErrorResponse> handleSQLException(SQLException e, HttpServ
ErrorResponse errorResponse = ErrorResponse.of(ErrorCode.INTERNAL_SQL_ERROR, request);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}

@ExceptionHandler(AmazonS3Exception.class)
public ResponseEntity<ErrorResponse> handleAmazonS3Exception(AmazonS3Exception e, HttpServletRequest request) {
log.error("발생한 에러: {}", e.getErrorCode());
log.error("에러가 발생한 지점 {}, {}", request.getMethod(), request.getRequestURI());
ErrorResponse errorResponse = ErrorResponse.of(ErrorCode.FILE_EXTENSION_FAULT, request);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}
}

10 changes: 4 additions & 6 deletions src/main/java/org/cotato/csquiz/common/util/FileUtil.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.cotato.csquiz.common.util;

import java.util.Arrays;
import java.util.List;
import org.cotato.csquiz.common.error.ErrorCode;
import org.cotato.csquiz.common.error.exception.ImageException;
import org.springframework.web.multipart.MultipartFile;

public class FileUtil {

private static final String[] ALLOWED_IMAGE_FILE_EXTENSIONS = {"png", "jpg", "jpeg", "heif"};
private static final List<String> ALLOWED_IMAGE_FILE_EXTENSIONS = List.of("png", "jpg", "jpeg", "heif");

public static String extractFileExtension(MultipartFile file) throws ImageException {
String originalFilename = file.getOriginalFilename();
Expand All @@ -18,9 +18,7 @@ public static String extractFileExtension(MultipartFile file) throws ImageExcept
return originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
}

public static void checkAllowedImageFileExtension(String fileExtension) throws ImageException {
if (!Arrays.asList(ALLOWED_IMAGE_FILE_EXTENSIONS).contains(fileExtension)) {
throw new ImageException(ErrorCode.FILE_EXTENSION_FAULT);
}
public static boolean isImageFileExtension(String fileExtension) {
return ALLOWED_IMAGE_FILE_EXTENSIONS.contains(fileExtension);
}
}

0 comments on commit aa4a99c

Please sign in to comment.