[TIL] 230826 ObjectMetadata 로 이미지 업로드하기

CountryGirl·2023년 8월 26일
0

TIL

목록 보기
50/80

📌 기존의 MultipartFile 을 File 로 변환해 업로드하는 방식

public class S3Service {

    private final AmazonS3Client amazonS3Client;

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


    // 파일을 S3에 업로드
    public List<String> upload(
            List<MultipartFile> multipartFiles,
            String dirName
    ) throws IOException {
        List<String> fileNames = new ArrayList<>();

        for (MultipartFile multipartFile : multipartFiles) {
            // MultipartFile -> File 변환
            File uploadFile = convert(multipartFile).orElseThrow(
                    () -> new S3Exception(ClientErrorCode.S3_CONVERT_FAILURE));
            String fileName = uploadFile.getName();
            upload(uploadFile, dirName);
            fileNames.add(fileName);
        }
        return fileNames;
    }

    // Overloading
    private String upload(File uploadFile, String dirName) {
        String s3FileName = dirName + "/" + uploadFile.getName();
        String uploadImageURL = putS3(uploadFile, s3FileName);

        boolean deleteSuccessful = uploadFile.delete(); // 임시로 저장된 이미지 삭제
        if (!deleteSuccessful) {
            throw new S3Exception(ClientErrorCode.S3_TEMP_IMAGE_DELETE_FAILURE);
        }
        return uploadImageURL;
    }

    // 실제로 S3로 업로드
    private String putS3(File uploadFile, String s3FileName) {
        amazonS3Client.putObject(
                new PutObjectRequest(s3Bucket, s3FileName, uploadFile)
                        .withCannedAcl(CannedAccessControlList.PublicRead));
        return amazonS3Client.getUrl(s3Bucket, s3FileName).toString();
    }

    public Optional<File> convert(MultipartFile multipartFile) throws IOException {

        String originalFileName = multipartFile.getOriginalFilename();

        if (originalFileName == null) {
            log.error("originalFileName == null");
            return Optional.empty();
        }
        String fileExtension = originalFileName
                .substring(originalFileName.lastIndexOf("."));

        String fileName = File.separator + originalFileName
                .substring(0, originalFileName.lastIndexOf(".")) + "-";
        // 유니크한 파일명 -> createTempFile 중복방지: 자체적으로 난수 생성
        File convertFile = File.createTempFile(fileName, fileExtension);

        if (convertFile.exists()) {
            try (FileOutputStream fileOutputStream = new FileOutputStream(convertFile)) {
                // fileOutputStream 데이터 -> 바이트 스트림으로 저장
                fileOutputStream.write(multipartFile.getBytes());
            }
            return Optional.of(convertFile);
        }
        throw new S3Exception(ClientErrorCode.S3_CONVERT_FAILURE);
    }

✅ 작동 순서

1. 파일 변환: MultipartFile 객체를 File 객체로 변환
2. 임시 파일 생성: 변환 과정에서 임시 파일이 생성 (원본 파일의 이름과 랜덤으로 생성된 난수를 결합한 이름으로 서버의 로컬 디스크에 저장)
3. S3 업로드: 임시 파일을 S3 에 업로드한다.
4. 임시 파일 삭제: S3 업로드가 완료된 후에는 서버의 로컬 디스크에서 임시 파일을 삭제

👍 장점

• 단계별로 어떠한 처리를 하는지 명확하게 판단할 수 있다.
• 각 단계마다 에러 핸들링을 할 수 있다.

👎 단점

• 임시 파일을 저장하면서 삭제가 되지 않을 경우 디스크 공간에 대한 이슈가 발생할 수 있다.
• 디스크에 파일을 쓰고 읽는 작업이기 때문에 작업량이 많아지게 된다면 서버의 전체적인 성능저하가 발생할 수 있다.


📌 ObjectMetadata 를 이용한 이미지 업로드 방식

@Service
public class S3Service {

    private final AmazonS3Client amazonS3Client;

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

    public List<String> upload(
            List<MultipartFile> images,
            String folderName
    ) throws IOException {
        List<String> fileNameList = new ArrayList<>();
        for (MultipartFile image : images) {
            String fileName = upload(image, folderName);
            fileNameList.add(fileName);
        }
        System.out.println("fileNameList = " + fileNameList);
        return fileNameList;
    }

    public String upload(
            MultipartFile multipartFile,
            String folderName
    ) throws IOException {
        String fileName = UUID.randomUUID().toString()
                .substring(19) + "-" + multipartFile.getOriginalFilename();
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(multipartFile.getSize());
        objectMetadata.setContentType(multipartFile.getContentType());
        amazonS3Client.putObject(
                new PutObjectRequest(s3Bucket, "images/" + folderName + "/"
                        + fileName, multipartFile.getInputStream(), objectMetadata)
                        .withCannedAcl(CannedAccessControlList.PublicRead)
        );

        return fileName;
    }

👍 장점

• 단계 간소화: MultipartFileFile 로 변환하는 과정, 임시 파일을 생성 및 삭제 등 추가적인 단계없이 바로 S3 에 업로드
• 에러 발생 가능성 감소: 단계가 간소화되면서 파일 변환, 임시 파일 관리(생성, 삭제) 등으로 인해 에러가 발생하지 않는다.
• 디스크 공간 절약: 서버의 디스크 공간을 사용하지 않아 자원 사용 면에서 효율적이고, 디스크에 파일을 쓰고 읽는 작업이 없으므로 시스템의 I/O 부하가 줄어들어 성능이 향상될 수 있다.

코드가 너무 간결해졌다. 가독성도 더 높아지고 이미지가 S3 업로드 될 때 걸리는 시간은 네트워크 상태에 따라 다르지만 MultipartFile을 File 로 변환하는 과정이 없기 때문에 더 빠를 것으로 예상된다.

profile
💻🌾시골소녀의 엉망징창 개발 성장일지🌾💻 (2023.05.23 ~)

0개의 댓글