프로젝트/잇타

AWS S3를 사용하여 파일업로드

SeoburiFaust 2022. 12. 27. 15:42

순서 요약

1. S3 bucket 생성

2. IAM 사용자 -> 정책 추가

3. gradle에 dependency 추가, yml 설정, S3UploaderService 생성, api 추가

 

1. S3 bucket을 생성한다.

다음 링크로 이동 -> https://s3.console.aws.amazon.com/s3/get-started?region=ap-northeast-2 

 

https://s3.console.aws.amazon.com/s3/get-started?region=ap-northeast-2

 

s3.console.aws.amazon.com

버킷 만들기로 이동 -> 버킷이름 리전 설정.

객체 소유권 활성화

 

 

퍼블릭 엑세스 차단 해제 (->정책 편집하기 위함. 정책 편집 후 다시 차단)

 

나머지 설정 그대로 버킷 생성

 

2.  IAM 생성

 

사용자 추가

 

사용자 이름 작성, 엑세스 키 선택

 

 기존 정책 직접 연결 -> amazonS3FullAccess 선택-> 나머지 단계 그대로 다음 이동 -> 사용자 생성

 

3 - 1. gradle에 dependency추가

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

3 - 2. yml 설정

#s3 설정
spring:
  profiles:
    group:
      "local": "local, common"
      "development": "development,common"
    active: local

---
# 공통
spring:
  config:
    activate:
      on-profile: "common"
  servlet:
    multipart:
      max-file-size: 5GB
      max-request-size: 5GB

cloud:
  aws:
    region:
      static: ap-northeast-2
    s3:
      bucket: {버킷이름}
    stack:
      auto: false
    credentials:
      instance-profile: true
      access-key: {엑세스키}
      secret-key: {시크릿키}

logging:
  level:
    com.amazonaws.util.EC2MetadataUtils: error

---
# 로컬 환경
spring:
  environment: "local"
  config:
    activate:
      on-profile: "local"
  file-dir: /src/main/resources/static/files/
---
# 배포 환경
spring:
  environment: "development"
  config:
    activate:
      on-profile: "development"
  file-dir: /home/ec2-user/files/

common은 공통으로 적용되는 속성,

local과 developement로 나누어 로컬 환경배포환경 설정을 달리한다.

각각 파일이 저장되는 경로를 지정해준다.

 

 

cloud.aws 설정에서 버킷이름과 IAM 생성시 발급해주는 엑세스 키, 시크릿 키를 잘 입력해준다.

 

3-3. S3UploaderService

@Slf4j
@Service
@RequiredArgsConstructor
public class S3UploaderService {

    // local, development 등 현재 프로파일
    @Value("${spring.environment}")
    private String environment;

    // 파일이 저장되는 경로
    @Value("${spring.file-dir}")
    private String basicDir;
    private String fileDir;

    private final AmazonS3Client amazonS3Client;

    /**
     * 서버가 시작할 때 프로파일에 맞는 파일 경로를 설정해줌
     */
    @PostConstruct
    private void init(){
        if(environment.equals("local")){
            this.fileDir = System.getProperty("user.dir") + this.basicDir;
        } else if(environment.equals("development")){
            this.fileDir = this.basicDir;
        }
    }

    public String upload(MultipartFile multipartFile, String bucket, String dirName) throws IOException {
        File uploadFile = convert(multipartFile)  // 파일 변환할 수 없으면 에러
                .orElseThrow(() -> new IllegalArgumentException("error: MultipartFile -> File convert fail"));

        return upload(uploadFile, bucket, dirName);
    }

    // S3로 파일 업로드하기
    private String upload(File uploadFile, String bucket, String dirName) {
        String fileName = dirName + "/" + UUID.randomUUID() + uploadFile.getName();   // S3에 저장된 파일 이름
        String uploadImageUrl = putS3(uploadFile, bucket, fileName); // s3로 업로드
        removeNewFile(uploadFile);
        return uploadImageUrl;
    }

    // S3로 업로드
    private String putS3(File uploadFile, String bucket, 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("File delete success");
            return;
        }
        log.info("File delete fail");
    }

    /**
     * @param multipartFile
     * 로컬에 파일 저장하기
     */
    private Optional<File> convert(MultipartFile multipartFile) throws IOException {
        if (multipartFile.isEmpty()) {
            return Optional.empty();
        }

        String originalFilename = multipartFile.getOriginalFilename();
        String storeFileName = createStoreFileName(originalFilename);

        //파일 업로드
        File file = new File(fileDir+storeFileName);
        multipartFile.transferTo(file);

        return Optional.of(file);
    }

    /**
     * @description 파일 이름이 이미 업로드된 파일들과 겹치지 않게 UUID를 사용한다.
     * @param originalFilename 원본 파일 이름
     * @return 파일 이름
     */
    private String createStoreFileName(String originalFilename) {
        String ext = extractExt(originalFilename);
        String uuid = UUID.randomUUID().toString();
        return uuid + "." + ext;
    }

    /**
     * @description 사용자가 업로드한 파일에서 확장자를 추출한다.
     *
     * @param originalFilename 원본 파일 이름
     * @return 파일 확장자
     */
    private String extractExt(String originalFilename) {
        int pos = originalFilename.lastIndexOf(".");
        return originalFilename.substring(pos + 1);
    }

}

설명 생략

 

3-4. api 추가

@PostMapping("/image-upload")
@ResponseBody
public String imageUpload(@RequestPart("file") MultipartFile multipartFile) throws IOException {
    return s3UploaderService.upload(multipartFile, "{버킷이름}", "image");
}

설명 생략

 

swagger로 통해 업로드 확인

 

파일을 선택하고 execute를 누르면 

성공!

 

S3 bucket에 들어가서 확인해보면

파일이 잘 저장된다. pdf, png 둘 다 가능하다.

 

 

자세한 설명은 다음 링크 :  https://jojoldu.tistory.com/300