(Spring boot + S3 + MySQL) → EC2 (1)

형석이의 성장일기·2023년 9월 11일
0

우리 프로젝트 특성 상, “음성파일 + 이미지” 이렇게 한 쌍은 무조건 DB에 저장해야 한다.

근데 저번 공통 프로젝트에서 범군에게 물어보니까 그냥 Ec2 상에 https://ec2도메인/이미지경로/이미지이름.jpg” 이렇게 저장한 다음에 DB에 저 경로를 저장하고, 불러오는 방식을 사용했다칸다.

사실 EC2 로컬에 이미지랑 음성파일을 저장해도 되긴 하는데, 이번엔 S3 한번 써보자!

그 전에 S3를 쓰는 이유에 대해 정리할께용

💡 **높은 내구도로 중요한 정보를 안심하고 저장할 수 있다.**

99.999의 객체 내구도를 가진 인프라이다. 여기서 내구도는 유실될 가능성을 나타낸다.

여러시설과 각 시설에 중복 저장 되므로 서버 하나가 파괴 되더라도 다른 저장소에 남아있다.

보안성이 뛰어나다.

SSL을 통해 데이터 전송과 암호화를 하므로 해킹 걱정이 적어진다.

이벤트 알림 전송

S3로 파일이 업로드 되었을 때, 그 사실을 다른 서비스에게 알려서, 서비스를 트리거할 수 있다.

즉, S3와 연계된 서비스들을 사용하는데 상당히 유용하다.

EC2 생성

SSAFY에서 ec2 서버 주기 전에 미리 테스트용으로 프리티어(Ubuntu) ec2 하나 만들자.

S3 버킷 생성

생성 완료

IAM 설정

IAM 이란? 사용자의 계정 또는 그룹에 따라 독립적으로 AWS 자원에 접근을 제어하고 권한을 제어하는 등의 자격 증명을 관리하는 서비스이다.

IAM 생성하는 페이지에서 “s3-user” 라는 사용자 이름 만들고,

정책으로 AmazonS3FullAccess를 설정해준다.

아래에 따르면 명칭 그대로 전체 액세스에 대한 권한이다.

다음 누르고 걍 하면, 이렇게 IAM 사용자가 생성됨

이제 저 사용자 화면에 드가서 “엑세스 키 만들기” 클릭하고 엑세스 키 만들면,

이런식으로 생성된다.

저 엑세스 키 발급될 때, 비밀 엑세스 키도 발급되니까 꼭 .csv 파일 저장하기


Spring Boot 프로젝트 생성 및 수정

디렉토리 구조

build.gradle의 dependencies에 이거 추가

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

application.yml

spring:
  profiles.include:
    - s3

application-s3.yml (이거 .gitignore에 추가해야됨)

cloud:
  aws:
    s3:
      bucket: 버킷이름
    credentials:
      access-key: csv 파일에 있는 access-key
      secret-key: csv 파일에 있는 secret-key
    region:
      static: ap-northeast-2
      auto: false
    stack:
      auto: false

FileuploadApplication.java

package s3test.fileupload;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FileuploadApplication {
	static {
		System.setProperty("com.amazonaws.sdk.disableEc2Metadata", "true");
	}
	public static void main(String[] args) {
		SpringApplication.run(FileuploadApplication.class, args);
	}
}

AwsS3Config.java

package s3test.fileupload;

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

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

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

AwsS3Uploader.java

package s3test.fileupload;

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

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

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.log4j.Log4j2;

@Log4j2
@RequiredArgsConstructor
@Component
public class AwsS3Uploader {

	private final AmazonS3Client amazonS3Client;

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

	public String upload(MultipartFile multipartFile, String dirName) throws IOException {
		File uploadFile = convert(multipartFile)        // 파일 생성
			.orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File convert fail"));

		return upload(uploadFile, dirName);
	}

	private String upload(File uploadFile, String dirName) {
		String fileName = dirName + "/" + UUID.randomUUID() + uploadFile.getName();
		String uploadImageUrl = putS3(uploadFile, fileName);    // s3로 업로드
		removeNewFile(uploadFile);
		return uploadImageUrl;
	}

	// 1. 로컬에 파일생성
	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();
	}

	// 2. S3에 파일업로드
	private String putS3(File uploadFile, String fileName) {
		amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, uploadFile).withCannedAcl(
			CannedAccessControlList.PublicRead));
		log.info("File Upload : " + fileName);
		return amazonS3Client.getUrl(bucket, fileName).toString();
	}

	// 3. 로컬에 생성된 파일삭제
	private void removeNewFile(File targetFile) {
		if (targetFile.delete()) {
			log.info("File delete success");
			return;
		}
		log.info("File delete fail");
	}

	public void delete(String fileName) {
		log.info("File Delete : " + fileName);
		amazonS3Client.deleteObject(bucket, fileName);
	}
}

FileController.java

package s3test.fileupload;

import java.io.IOException;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RestController
public class FileController {
	private final AwsS3Uploader awsS3Uploader;

	@PostMapping("/upload")
	public String upload(@RequestParam("file") MultipartFile multipartFile) throws IOException {
		String fileName = awsS3Uploader.upload(multipartFile, "test");
		return fileName;
	}
}

결과는?

저장 잘된다

내일은 이거랑 DB, EC2 연결해보자

profile
이사중 .. -> https://gudtjr2949.tistory.com/

0개의 댓글