[스프링] AWS S3에 이미지 업로드 하기
Spring Boot

[스프링] AWS S3에 이미지 업로드 하기

728x90

 

 

spring boot로 서비스를 구축할 때 반드시 구축해야 할 것이 정적 파일 업로더이다.

이번 포스팅에는 AWS S3와 연동하여 이미지를 업로드 해볼 것이다.

 

 

AWS S3 bucket 생성

[AWS -> S3 -> 버킷 만들기]로 들어와 새로운 버킷을 만든다.

 

버킷을 만들 때, 권한을 수정한다. 권한을 수정하지 않고 파일을 업로드하면, 퍼블릭 액세스가 차단된 상태이기 때문에 아래의 403 에러가 발생한다.

Access Denied (Service: Amazon S3; Status Code: 403; Error Code: AccessDenied; Request ID: ~~~ ; S3 Extended Request ID: ~~~)

따라서 예제에서는 퍼블릭 엑세스 차단 해제를 하고, 버킷 정책을 설정하는 방법을 사용할 것이다.

 

버킷을 생성한 뒤, [권한 -> 버킷 정책]으로 들어가서 정책을 설정한다.

{
    "Version": "2012-10-17",
    "Id": "Policy1580913852302",
    "Statement": [
        {
            "Sid": "Stmt1580913849359",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::[버킷 이름]/*"
        }
    ]
}

 

 

 

AWS IAM 사용자 생성

생성한 S3에 접근하기 위해서는 권한이 필요하다. IAM 사용자로 등록하여 접근을 할 것이다.

 

권한 설정에서 s3를 검색하여 AmazonS3FullAccess를 선택한다.

 

사용자를 생성하면 Access key와 Secret key를 부여받는다. 이것을 이제 메모장에 복사해 놓고 나중에 사용할 것이다.



 

build.gradle

의존성을 주입 받기 위해 build.gradle에 아래의 코드를 추가한다.

repositories {
    mavenCentral()
    maven { url 'https://repo.spring.io/libs-milestone'}
}

dependencies {
    ...
    
    implementation 'org.springframework.cloud:spring-cloud-starter-aws'
    
    ...
}

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-aws:2.2.1.RELEASE'
    }
}

 

 

S3Upload.java

S3에 정적 파일을 올리는 기능을 하는 S3Upload.java를 생성한다.

package com.leveloper.chacha.springbootchacha.aws;

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.web.multipart.MultipartFile;

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

@Slf4j
@RequiredArgsConstructor
@Component
public class S3Uploader {

    private final AmazonS3Client 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로 전환이 실패했습니다."));

        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("파일이 삭제되었습니다.");
        } else {
            log.info("파일이 삭제되지 못했습니다.");
        }
    }

    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();
    }
}

 

 

S3Controller.java

S3Upload를 테스트할 Controller를 생성한다.

package com.leveloper.chacha.springbootchacha.aws;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@RequiredArgsConstructor
@Controller
public class S3Controller {
    private final S3Uploader s3Uploader;

    @GetMapping("/test")
    public String index() {
        return "test";
    }

    @PostMapping("/upload")
    @ResponseBody
    public String upload(@RequestParam("data") MultipartFile multipartFile) throws IOException {
        return s3Uploader.upload(multipartFile, "static");
    }
}

 

test.html

<!DOCTYPE HTML>
<html>
<head>
    <title> SpringBoot & AWS S3</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    <script
            src="https://code.jquery.com/jquery-3.3.1.js"
            integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
</head>
<body>
<h1>
    S3 이미지 업로더
</h1>
<div class="col-md-12">
    <div class="col-md-2">
        <form>
            <div class="form-group">
                <label for="img">파일 업로드</label>
                <input type="file" id="img">
            </div>
            <button type="button" class="btn btn-primary" id="btn-save">저장</button>
        </form>
    </div>
    <div class="col-md-10">
        <p><strong>결과 이미지입니다.</strong></p>
        <img src="" id="result-image">
    </div>
</div>


<script>
    $('#btn-save').on('click', uploadImage);

    function uploadImage() {
        var file = $('#img')[0].files[0];
        var formData = new FormData();
        formData.append('data', file);

        $.ajax({
            type: 'POST',
            url: '/upload',
            data: formData,
            processData: false,
            contentType: false
        }).done(function (data) {
            $('#result-image').attr("src", data);
        }).fail(function (error) {
            alert(error);
        })
    }
</script>
</body>
</html>

 

테스트

파일을 선택하여 [저장] 버튼을 누르면 정상적으로 업로드가 된다.

S3에 들어가서 정상적으로 업로드된 것을 확인할 수 있다.

 

 

 

 

참고

https://victorydntmd.tistory.com/334

https://jojoldu.tistory.com/300

728x90