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

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

전에 쓴 코드를 좀만 바꾸자

우리는 이미지와 오디오파일을 함께 저장해야하니까

그것이 약속이니까

디렉토리 구조

application-db.yml (이것도 .gitignore에 추가하기)

# MySQL 설정
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://DB_IP:DB_PORT/DB_NAME?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul
    username: DB_USERNAME
    password: DB_PASSWORD

  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true

application.yml

spring:
  profiles.include:
    - s3
    - db

build.gradle의 dependencies 에 추가

runtimeOnly 'com.mysql:mysql-connector-j'
// 테스트 의존성 추가
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
// P6Spy 쿼리 파라미터 로그 외부라이브러리 사용
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'

domain/Test.java

package s3test.fileupload.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity(name="test")
public class Test {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int idx;

	private String image;
	private String audio;
}

repository/TestRepository.java

package s3test.fileupload.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import s3test.fileupload.domain.Test;

@Repository
public interface TestRepository extends JpaRepository<Test, Integer> {
}

service/TestService.java

package s3test.fileupload.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import s3test.fileupload.domain.Test;
import s3test.fileupload.repository.TestRepository;

@Service
public class TestService {

	@Autowired
	private TestRepository testRepository;

	public void save(String[] imageFileNames, String[] audioFileNames) {

		for (int i = 0; i < imageFileNames.length; i++) {
			testRepository.save(Test.builder()
				.image(imageFileNames[i])
				.audio(audioFileNames[i])
				.build());
		}
	}
}

FileController.java

package s3test.fileupload;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

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;
import s3test.fileupload.service.TestService;

@RequiredArgsConstructor
@RestController
public class FileController {

	private final AwsS3Uploader awsS3Uploader;
	private final TestService testService;

	@PostMapping("/upload")
	public List<String[]> upload(@RequestParam("image") MultipartFile[] imageMultipartFile,
		@RequestParam("audio") MultipartFile[] audioMultipartFile) throws IOException {
		List<String[]> result = new ArrayList<>();

		String[] imageFileNames = awsS3Uploader.upload(imageMultipartFile, "image");
		String[] audioFileNames = awsS3Uploader.upload(audioMultipartFile, "audio");

		testService.save(imageFileNames, audioFileNames);

		result.add(imageFileNames);
		result.add(audioFileNames);

		return result;
	}
}

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[] uploadFiles = new File[multipartFile.length];

		for (int i = 0 ; i < multipartFile.length ; i++) {
			uploadFiles[i] = convert(multipartFile[i])
				.orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File convert fail")); // 파일 생성
		}

		return upload(uploadFiles, dirName);
	}

	private String[] upload(File[] uploadFiles, String dirName) {
		String[] uploadUrls = new String[uploadFiles.length];

		for (int i = 0 ; i < uploadFiles.length ; i++) {
			String fileName = dirName + "/" + UUID.randomUUID() + uploadFiles[i].getName();
			uploadUrls[i] = putS3(uploadFiles[i], fileName);    // s3로 업로드
			removeNewFile(uploadFiles[i]);
		}

		return uploadUrls;
	}

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

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

이러고 서버 킨 다음에, Postman 테스트 해보면,

S3 들어가서 확인해보면,

DB에도 S3 경로가 잘 저장되었다

이제 이걸 EC2에 올려보자..

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

0개의 댓글