[Spring boot] AWS S3 를 이용한 파일 업로드 - 2 (S3-부트 연결)
2023.09.26 - [분류 전체보기] - [Spring boot] AWS S3 를 이용한 파일 업로드 - 1 (S3 생성 + 설정)
[Spring boot] AWS S3 를 이용한 파일 업로드 - 1 (S3 생성 + 설정)
프로젝트에서 파일을 로컬로 저장해서 사용하고 있었는데 치명적인 문제점이 있었다. 만약 사진을 업로드 하게 되면 파일이 제대로 뜨지 않았다. 사진은 정적파일이기 때문이라는 문제도 있었
sean-lets-go.tistory.com
저번 글에서는 S3를 사용하게된 이유와 S3 생성 +설정을 해보았습니다.
요번 글에서는 S3와 스프링 부트(프로젝트)와 연결해보려고 합니다.
스프링 연동하기
1) build.gradle에 의존성 추가
//S3 연동
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
2) application.properties 에 설정 정보 추가
cloud.aws.credentials.accessKey=<발급받은 accessKey>
cloud.aws.credentials.secretKey=<발급받은 secretKey>
cloud.aws.s3.bucket=<버킷이름>
cloud.aws.region.static=ap-northeast-2
cloud.aws.stack.auto=false
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
accessKey 와 같은 보안 설정 정보는 해킹 위험이 있으므로 별도의 설정 파일로 분리 후 git ignore 해주어야 합니다.
설정 파일에 있는 정보는 스프링이 지원하는 @Value 어노테이션을 사용하여 불러올 수 있습니다.
cloud.aws.stack.auto=false
EC2에서 Spring Cloud 프로젝트를 실행시키면 기본으로 CloudFormation 구성을 시작하기 때문에 설정한 CloudFormation이 없으면 프로젝트 실행이 되지 않습니다. 해당 기능을 사용하지 않도록 false로 설정.
cloud.aws.region.static:ap-northeast-2
지역을 한국으로 고정
multipart
기본적으로 default의 값은 1MB 이다. 사진을 업로드할 때에 최대 10MB의 원본을 가지고 있으므로 10MB로 설정
3) 스프링 설정 추가
S3Config.java
@Configuration
public class S3Config {
@Value("${cloud.aws.credentials.accessKey}")
private String accessKey;
@Value("${cloud.aws.credentials.secretKey}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
@Bean
public AmazonS3 amazonS3Client(){
AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(region)
.build();
}
}
application.properties에서 설정한 값을 Value로 주입해서 사용합니다,
4) AmazonS3Controller 생성
@RestController
@RequiredArgsConstructor
@RequestMapping("/s3")
public class AmazonS3Controller {
private final AwsS3Service awsS3Service;
/**
* Amazon S3에 파일 업로드
* @return 성공 시 200 Success와 함께 업로드 된 파일의 파일명 리스트 반환
*/
@ApiOperation(value = "Amazon S3에 파일 업로드", notes = "Amazon S3에 파일 업로드 ")
@PostMapping("/file")
public ResponseEntity<List<String>> uploadFile(@ApiParam(value="파일들(여러 파일 업로드 가능)", required = true) @RequestPart List<MultipartFile> multipartFile) {
return ApiResponse.success(awsS3Service.uploadImage(multipartFile));
}
/**
* Amazon S3에 업로드 된 파일을 삭제
* @return 성공 시 200 Success
*/
@ApiOperation(value = "Amazon S3에 업로드 된 파일을 삭제", notes = "Amazon S3에 업로드된 파일 삭제")
@DeleteMapping("/file")
public ResponseEntity<Void> deleteFile(@ApiParam(value="파일 하나 삭제", required = true) @RequestParam String fileName) {
awsS3Service.deleteImage(fileName);
return ApiResponse.success(null);
}
}
5) AmazonS3Controller 생성
@Service
@RequiredArgsConstructor
public class AwsS3Service {
@Value("${cloud.aws.s3.bucket}")
private String bucket;
private final AmazonS3 amazonS3;
public List<String> uploadFile(List<MultipartFile> multipartFile) {
List<String> fileNameList = new ArrayList<>();
// forEach 구문을 통해 multipartFile로 넘어온 파일들 하나씩 fileNameList에 추가
multipartFile.forEach(file -> {
String fileName = createFileName(file.getOriginalFilename());
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
try(InputStream inputStream = file.getInputStream()) {
amazonS3.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch(IOException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "파일 업로드에 실패했습니다.");
}
fileNameList.add(fileName);
});
return fileNameList;
}
public void deleteFile(String fileName) {
amazonS3.deleteObject(new DeleteObjectRequest(bucket, fileName));
}
private String createFileName(String fileName) { // 먼저 파일 업로드 시, 파일명을 난수화하기 위해 random으로 돌립니다.
return UUID.randomUUID().toString().concat(getFileExtension(fileName));
}
private String getFileExtension(String fileName) { // file 형식이 잘못된 경우를 확인하기 위해 만들어진 로직이며, 파일 타입과 상관없이 업로드할 수 있게 하기 위해 .의 존재 유무만 판단하였습니다.
try {
return fileName.substring(fileName.lastIndexOf("."));
} catch (StringIndexOutOfBoundsException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "잘못된 형식의 파일(" + fileName + ") 입니다.");
}
}
}
6) View에서 Test
프로젝트에서 S3를 구현한 부분입니다.
AWS S3 콘솔에서 파일이 들어온 것을 확인할 수 있습니다.
S3를 사용하지 않고 로컬저장을 했을 때는 application이 재실행 되어야 새로운 사진이 등장했는데,
지금은 S3를 이용하여 사진이 바로 등장하는것을 볼 수 있습니다!
view 파일에 대해서 궁금한게 있으시면 댓글주세요!!