AWS

spring boot s3 업로드

JUNGKEUNG 2021. 6. 2. 10:51

S3 업로드 시작 전 확인


버킷 생성  : https://jungkeung.tistory.com/44

버킷 정책 생성 :  https://jungkeung.tistory.com/45

IAM 키 생성 :  https://jungkeung.tistory.com/46

 

 

 

환경 세팅


- Intellij 2021.01

- SpringBoot 2.2.4

- Gralde

- spring-cloud-aws 2.2.1

 

 

 

build.gralde


- thymeleaf, lombok, JPA, Mysql, web 정도 입니다.

dependencies {
    implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.1.RELEASE'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'junit:junit:4.12'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }


    // AWS S3
    compile group: 'org.springframework.cloud', name: 'spring-cloud-aws', version: '2.2.1.RELEASE', ext: 'pom'
    compileOnly 'org.projectlombok:lombok:1.18.12'
    annotationProcessor 'org.projectlombok:lombok:1.18.12'
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile("org.springframework.boot:spring-boot-starter-jdbc")
    compile("mysql:mysql-connector-java:8.0.23")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    testCompile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

 

 

 

aws 설정


aws 설정은 applcation,yml 파일에 작성 했습니다.

 

src/main/resources/application.yml

cloud:
  aws:
    credentials:
      accessKey: 본인 ACCESS_KEY
      secretKey: 본인 SECREY_KEY
    s3:
      bucket: 본인 버켓 이름
    region:
      static: 본인 설정 서버 (ex: ap-northeast-2)
    stack:
      auto: false

위에 stack.auto는 Spring Cloud 실행 시, 서버구성을 자동화하는 CloudFormation이 자동으로 실해되는데 이를 사용하지않겠다는 설정입니다. 안하시면 에러가 뜹니다

 

 

 

gitignore


- gitgnore 에 application.yml 넣어주셔야합니다.

git에 aws 키가 노출되면 엄청난 과금이 생기니 꼭 해주셔야합니다.

.gitgnore 하고 push를 하기 전에, git status로 application.yml 파일이 commit목록에 없는지 꼭확인하세요.

 

이제 설정이 끝났으니 코드를 구현하겠습니다

 

 

 

서비스 구현 - 퍼블리싱


src/main/resources/templates/gallery.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>S3 파일 업로드 테스트</title>
</head>
<body>
    <h1>파일 업로드</h1> <hr>

    <form th:action="@{/gallery}" method="post" enctype="multipart/form-data">
        제목 : <input type="text" name="title"> <br>
        파일 : <input type="file" name="file"> <br>
        <button>등록하기</button>
    </form>

</body>
</html>

 

 

 

서비스 구현 -Controller

src/main/java/com/victolee/s3exam/controller/GalleryController.java

import com.victolee.s3exam.dto.GalleryDto;
import com.victolee.s3exam.service.GalleryService;
import com.victolee.s3exam.service.S3Service;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@Controller
@AllArgsConstructor
public class GalleryController {
    private S3Service s3Service;
    private GalleryService galleryService;

    @GetMapping("/gallery")
    public String dispWrite() {

        return "/gallery";
    }

    @PostMapping("/gallery")
    public String execWrite(GalleryDto galleryDto, MultipartFile file) throws IOException {
        String imgPath = s3Service.upload(file);
        galleryDto.setFilePath(imgPath);



        galleryService.savePost(galleryDto);

        return "redirect:/gallery";
    }
}

- execWrite(GalleryDto galleryDto, MultipartFile file) throws IOException

  form으로부터 넘어온 파일 객체를 받기 위해,MultipartFile 타입의 파라미터를 작성해줍니다.

- s3Service.upload(file)

  s3Service는 AWS S3의 비즈니스 로직을 담당하며, 파일을 조작합니다.

- galleryService.savePost(galleryDto);

 galleryService는 DB에 데이터를 조작하기 위한 서비스입니다.

 

 

 

서비스 구현 -S3Service


src/main/java/com/victolee/s3exam/service/S3Service.java

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;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.PutObjectRequest;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;

@Service
@NoArgsConstructor
public class S3Service {
    private AmazonS3 s3Client;

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

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

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

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

    @PostConstruct
    public void setS3Client() {
        AWSCredentials credentials = new BasicAWSCredentials(this.accessKey, this.secretKey);

        s3Client = AmazonS3ClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(this.region)
                .build();
    }

    public String upload(MultipartFile file) throws IOException {
        String fileName = file.getOriginalFilename();

        s3Client.putObject(new PutObjectRequest(bucket, fileName, file.getInputStream(), null)
                .withCannedAcl(CannedAccessControlList.PublicRead));
        return s3Client.getUrl(bucket, fileName).toString();
    }
}

AmazonS3Client가 deprecated됨에 따라, AmazonS3ClientBuilder를 사용했습니다. 

@Value("${cloud.aws.credentials.accessKey}")

 - lombok 패키지가 아닌, org.springframework.beans.factory.annotation 패키지임에 유의합니다.

 - 해당 값은 application.yml에서 작성한 cloud.aws.credentials.accessKey 값을 가져옵니다.

@PostConstruct

 - 의존성 주입이 이루어진 후 초기화를 수행하는 메서드이며, bean이 한 번만 초기화 될수 있도록 해줍니다.

 - 이렇게 해주는 목적은 AmazonS3ClientBuilder를 통해 S3 Client를 가져와야 하는데, 자격증명을 해줘야 S3 Client를        가져올 수 있기 때문입니다.

 s3Client.putObject(new PutObjectRequest(bucket, fileName, file.getInputStream(), null)

  - .withCannedAcl(CannedAccessControlList.PublicRead));

  - 외부에 공개할 이미지이므로, 해당 파일에 public read 권한을 추가합니다.

 s3Client.getUrl(bucket, fileName).toString()

  - 업로드를 한 후, 해당 URL을 DB에 저장할 수 있도록 컨트롤러로 URL을 반환합니다.

 

 

 

서비스 구현 -GalleryService


src/main/java/com/victolee/s3exam/service/GalleryService.java

import com.victolee.s3exam.domain.repository.GalleryRepository;
import com.victolee.s3exam.dto.GalleryDto;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
public class GalleryService {
    private GalleryRepository galleryRepository;

    public void savePost(GalleryDto galleryDto) {
        galleryRepository.save(galleryDto.toEntity());
    }
}

DB에 저장하는 로직입니다

 

 

 

서비스 구현 - GalleryRepository


src/main/java/com/victolee/s3exam/domain/repository/GalleryRepository.java

import com.victolee.s3exam.domain.entity.GalleryEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface GalleryRepository extends JpaRepository<GalleryEntity, Long> {
}

 

 

 

서비스 구현 -GalleryEntity


src/main/java/com/victolee/s3exam/domain/entity/GalleryEntity.java

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
@Table(name = "gallery")
public class GalleryEntity {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    @Column(length = 50, nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT")
    private String filePath;

    @Builder
    public GalleryEntity(Long id, String title, String filePath) {
        this.id = id;
        this.title = title;
        this.filePath = filePath;
    }
}

 

 

 

서비스 구현 - GalleryDto


src/main/java/com/victolee/s3exam/dto/GalleryDto.java

import com.victolee.s3exam.domain.entity.GalleryEntity;
import lombok.*;

@Getter
@Setter
@ToString
@NoArgsConstructor
public class GalleryDto {
    private Long id;
    private String title;
    private String filePath;

    public GalleryEntity toEntity(){
        GalleryEntity build = GalleryEntity.builder()
                .id(id)
                .title(title)
                .filePath(filePath)
                .build();
        return build;
    }

    @Builder
    public GalleryDto(Long id, String title, String filePath) {
        this.id = id;
        this.title = title;
        this.filePath = filePath;
    }
}

 

 

 

테스트


1) 파일 업로드

mysql Table 

제대로 들어온걸 확인 할수 있습니다

 

더 정확한 내용이나 정보는 https://victorydntmd.tistory.com/334 여기서 확인 하실수 있습니다!

'AWS' 카테고리의 다른 글

Linux 자동 배포  (0) 2021.06.12
Linux 배포 에러  (0) 2021.06.12
s3 IAM 설정하기  (0) 2021.05.31
s3 버킷 정책 생성  (0) 2021.05.31
s3 버킷 생성  (0) 2021.05.31