[3일차] aws S3

Woozy9ucci·2022년 10월 24일
1

미니 프로젝트 진행 중 이미지를 업로드 하는 기능을 구현하기로 했다.

이미지는 s3서버에 저장하고 url을 받아 DB에 저장하는 형식으로 진행되는 것 같았다.

가장먼저 AWS에서 버킷 (S3서버인스턴스) 을 생성해 주었다.

그리고 나서 이 버킷을 사용할 수 있는 계정 (IAM)을 생성해 주었다.

IAM의 권한과 버킷의 접근권한등을 설정해주었다.

다음은 그와 관련된 코드소스들이다.

  • 우선 aws cloud starter 의존성을 추가해주고
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
  • application.properties에 다음 사항들을 추가해 줬다.
cloud.aws.region.static='리전' (서울의 경우 ap-northeast-2)
cloud.aws.stack.auto=false
cloud.aws.s3.bucket='버킷이름'
cloud.aws.credentials.access-key='IAM 키'
cloud.aws.credentials.secret-key='IAM PW'

spring.servlet.multipart.max-file-size: 10MB
spring.servlet.multipart.max-request-size: 10MB
  • 이제 S3의 접속하기 위한 config를 작성하고 bean으로 등록해주었다.
@Configuration
public class AwsS3Config {
    @Value("${cloud.aws.credentials.access-key}")
    private String accessKey;
    @Value("${cloud.aws.credentials.secret-key}")
    private String secretKey;
    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3Client amazonS3Client() {
        BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey,secretKey);
        return (AmazonS3Client) AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
                .build();
    }
}
  • 파일을 업로드 할 때 사용될 각종 기능들이 있는 S3Uploader를 살펴보자
@Component
@RequiredArgsConstructor
public class S3Uploader {
    private final AmazonS3Client amazonS3Client;
    @Value("${cloud.aws.s3.bucket}")
    private String bucket;

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

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

    // S3로 업로드
    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()) {
            System.out.println("File delete success");
            return;
        }
        System.out.println("File delete fail");
    }

    // 로컬에 파일 업로드 하기
    private Optional<File> convert(MultipartFile file) throws IOException {
        File convertFile = new File(System.getProperty("user.dir") + "/" + file.getOriginalFilename());
        if (convertFile.createNewFile()) { // 바로 위에서 지정한 경로에 File이 생성됨 (경로가 잘못되었다면 생성 불가능)
            try (FileOutputStream fos = new FileOutputStream(convertFile)) { // FileOutputStream 데이터를 파일에 바이트 스트림으로 저장하기 위함
                fos.write(file.getBytes());
            }
            return Optional.of(convertFile);
        }
        return Optional.empty();
    }
}

이미지를 MultipartFile 타입으로 받아오고 S3에 업로드 하기 위해 로컬에 저장했다가 삭제하는 로직등이 들어있다.

  • 컨트롤러에서 다음과 같이 이미지를 받아왔다.
@PostMapping("/posts")
    public GlobalResponseDto createPost(@RequestPart(value = "img", required = false) MultipartFile multipartFile,
                                        @RequestPart(value = "post") @Valid PostRequestDto postRequestDto,
                                        @AuthenticationPrincipal UserDetailsImpl userDetails) throws IOException {
        if (userDetails==null){
            throw new CustomException(ErrorCode.NotFoundToken);
        }
        return postService.createPost(multipartFile, postRequestDto, userDetails.getAccount());
    }
  • 서비스에서는 다음과 같이 upload와 동시에 url을 String으로 받아오고 DB에 함께 저장해주었다.
String img = s3Uploader.uploadFiles(multipartFile, "testdir1");

급하게 구현하는데는 성공했지만 request를 formData형식으로 받을 때 Json을 따로받는것 보다 항목 하나하나를 key:value로 받는 것이 일반적이라고 한다.
이러한 부분들에 대해 조금더 구현해봐야겠다.

다음을 참고하여 진행하였다.
https://velog.io/@_koiil/Springboot-AWS-S3%EB%A1%9C-%ED%8C%8C%EC%9D%BC-%EC%A0%80%EC%9E%A5%EC%86%8C-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0
https://studyandwrite.tistory.com/498

0개의 댓글