[SpringBoot] AWS S3 이미지 업로드 - 1

Tech/Java,Kotlin,Spring 2023. 5. 26. 06:29
728x90
728x90

토이 프로젝트를 하면서 기록 남겨놓으면 좋을것 같아서 포스팅

 

 

AWS 가입, AWS S3 BUCKET 생성과 IAM KEY 발급 과정은 생략하겠음.

 

 

트러블 슈팅

https://mag1c.tistory.com/354

https://mag1c.tistory.com/355 

https://mag1c.tistory.com/356 

 

 

개발 환경

SpringBoot3.0.6

 

 

 

build.gradle DI

implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

 

 

 

aplication.properties

# S3
cloud.aws.credentials.accessKey=YourKey
cloud.aws.credentials.secretKey=YourKey
cloud.aws.s3.bucket=YourBucket
cloud.aws.s3.domain=YourDomain
cloud.aws.region.static=ap-northeast-2
cloud.aws.stack.auto=false

 

 

 

Config 생성

package com.uu.uni.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

@Configuration
public class S3Config {
    @Value("${cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secretKey}")
    private String secretKey;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3 amazonS3Client() {
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        return AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(region)
                .build();
    }

}

 

 

 

 

Uploader 객체 생성

로컬에 생성되는 이미지 파일은 삭제된다.(MultipartFile을 File로 전환하는 과정에서 파일이 생성된다.)

AmazonS3Client가 deprecated되어, AmazonS3을 사용했다 (필드)

package com.uu.uni.user.service;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.PutObjectRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

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

@Slf4j
@RequiredArgsConstructor
@Component
@Service
public class S3Uploader {

    private final AmazonS3 amazonS3Client;

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

    public String upload(MultipartFile multipartFile, String dirName) throws IOException {
        File uploadFile = convert(multipartFile)
                .orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File change fail"));
        return upload(uploadFile, dirName);
    }

    private String upload(File uploadFile, String dirName) {
        String fileName = dirName + "/" + uploadFile.getName();
        String uploadImageUrl = putS3(uploadFile, fileName);

        removeNewFile(uploadFile);

        return uploadImageUrl; 
    }

    private String putS3(File uploadFile, String fileName) {
        amazonS3Client.putObject(
                new PutObjectRequest(bucket, fileName, uploadFile)
                        .withCannedAcl(CannedAccessControlList.PublicRead)
        );
        return amazonS3Client.getUrl(bucket, fileName).toString();
    }

    private void removeNewFile(File targetFile) {
        if(targetFile.delete()) {
            log.info("delete success.");
        }else {
            log.info("delete fail.");
        }
    }

    private Optional<File> convert(MultipartFile file) throws  IOException {
        File convertFile = new File(file.getOriginalFilename());
        if(convertFile.createNewFile()) {
            try (FileOutputStream fos = new FileOutputStream(convertFile)) {
                fos.write(file.getBytes());
            }
            return Optional.of(convertFile);
        }
        return Optional.empty();
    }

}

 

 

 

View, Controller, Service 작성

1. View + JS (파일 보내기)

<form th:method="post" th:action="@{|/users/img/${idx}|}" name="imgchange" enctype="multipart/form-data">
    <input type="file" name="imgfile" style="display : none;">
    <input type="hidden" name="idx" th:value="${idx}">		
    <input type="hidden" name="img">
</form>
const img_upload = document.querySelector(".img_upload");
const input_file = document.querySelector("input[type='file']");
const filename = document.querySelector("input[name='img']");

function imgUpload(){
	input_file.click();
}

img_upload.addEventListener('click', function(){
	imgUpload();
});

input_file.addEventListener('change', function(){
	filename.value = this.files[0];	
	document.imgchange.submit();
});

 

2. Controller, Service

@ResponseBody
@PostMapping("/img/{idx}")
public String img_modify(@PathVariable int idx, @RequestParam("imgfile") MultipartFile imgfile, UserDTO dto) throws IOException {
    if(userService.img_modify(imgfile, dto)) return "성공";
    else return "실패";
}

 

컨트롤러에서, 성공 실패 여부를 확인하기 위해 ResponseBody로 걍 Text를 찍어보려고 했다.

 

public boolean img_modify(MultipartFile imgfile, UserDTO dto) throws IOException;
@Transactional
@Override
public boolean img_modify(MultipartFile imgfile, UserDTO dto) throws IOException {
    if(!imgfile.isEmpty()) {			
        String uuid = UUID.randomUUID().toString();	        
        String serverFileName = s3Uploader.upload(imgfile, dto.getId()+uuid);
        System.out.println(imgfile + "  :  " + dto.getId()+uuid);
        System.out.println("serverFileName : " + serverFileName);
        dto.setImg(serverFileName);

        Optional<UserEntity> User = userRepository.findByIdx(dto.getIdx());
        User.get().setImg(serverFileName);
        return true;
    }		
    else return false;		
}

 

서비스에서 위의 S3Uploader를 통해 AWS S3에 업로드 된다.

 

 

결과

 

 

 

 

잘 들어오지만, 업로드 할 때마다 디렉토리가 생성되어, 이를 수정할 생각이다.

728x90
300x250
mag1c

mag1c

2년차 주니어 개발자.

방명록