코딩블로그
[Spring] Amazons S3 Presigned Url 구현하기 본문
팝콘메이트에서 프론트 분들과 통신을 진행하면서 갑자기 image를 "Multipart"로 전송을 하면 500에러가 뜬다는 오류를 전달 받았다. 단순한 스크린 샷은 업로드가 수월했지만, 폰의 카메라에서 찍은 사진을 업로드 하려고 할 때 용량 문제로 업로드가 되지 않고 있었다.
이에 대한 해결책을 찾던 도중, image api를 따로 만들어서 S3에 pre-sign된 링크를 발급 받아 그 곳으로 이미지를 PUT하는 방식, 즉 Presigned Url 방식으로 바꿔 문제를 해결하였다.
1. 기존의 Multipart방식
S3에 하나의 대용량 파일을 업로드할 때 하나의 객체를 여러 부분으로 나누어 처리량을 향상 시키고 업로드를 실패한다면 빠른 복구를 한다는 장점이 있다.
나누어 처리를 하여 나누어진 조각들을 S3로 전송을 하고 모든 조각이 전송이 완료가 되면 하나의 Object File로 합친다
AWS 기준으로는 기본 용량을 제한하고 있어 임의로 자신이 원하는 크기 만큼 설정 할 수 있다
예시
sprgin:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
하지만 나는 이 방식을 택해도 오류가 해결되지 않아 아예 Presigned Url로 눈을 돌렸다
2. Pre-signed Url 방식
이 방식을 사용하면 서버의 네트워크 IO비용을 줄이고 자원을 절약할 수 있다. 또한, 무분별하게 S3에 접근하는 것을 막기에 보안적으로 강화가 된 접근 방식이다.
3.전체 코드
ScreeningController Class
@Operation(description = "모임 대표 이미지")
@GetMapping(value = "/image/{fileName}")
public SuccessResponse<Object> uploadImage(@PathVariable("fileName") String fileName) throws IOException {
try {
String imageUrl = screeningUploadUseCase.uploadImage("images",fileName);
SuccessResponse<Object> successResponse = SuccessResponse.onSuccess(200,imageUrl);
return successResponse;
} catch (IOException e) {
throw new IllegalArgumentException("오류");
}
}
ScreeningUploadUseCase Class
public String uploadImage(String prefix, String fileName) throws IOException {
// S3에 이미지 파일 업로드 및 업로드된 파일의 URL 생성
String imageUrl = s3UploadService.getPreSignedUrl(prefix,fileName);
return imageUrl;
}
S3UploadService Class
public String getPreSignedUrl(String prefix, String fileName) {
if (!prefix.isEmpty()) {
fileName = createPath(prefix, fileName);
}
GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(fileName);
URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest);
return url.toString();
}
private String createFileId() {
return UUID.randomUUID().toString();
}
private String createPath(String prefix, String fileName) {
String fileId = createFileId();
return String.format("%s/%s", prefix, fileId + "-" + fileName);
}
private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String fileName) {
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(bucket, fileName)
.withMethod(HttpMethod.PUT)
.withExpiration(getPreSignedUrlExpiration());
generatePresignedUrlRequest.addRequestParameter(
Headers.S3_CANNED_ACL,
CannedAccessControlList.PublicRead.toString());
return generatePresignedUrlRequest;
}
private Date getPreSignedUrlExpiration() {
Date expiration = new Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += 1000 * 60 * 2;
expiration.setTime(expTimeMillis);
log.info(expiration.toString());
return expiration;
}
S3Config Class
@Configuration
public class S3Config {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Bean
public AmazonS3 amazonS3Client() {
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder.standard()
.withRegion(Regions.AP_NORTHEAST_2)
.withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials))
.build();
}
}
application.yml
cloud:
aws:
s3:
bucket: popcornmate-bucket
credentials:
access-key: ${S3_ACCESS}
secret-key: ${S3_SECRET}
region:
static: ap-northeast-2
auto: false
stack:
auto: false
4. 실행 화면
원하는 file이름을 지정해주면
ResponseBody에 Presigned-url이 data로 넘어온다
이 url을 가지고 PUT 메서드를 이용하여 실제 이미지 파일을 이미지를 업로드하면 200 OK라는 성공 응답이 온다
실제로 website에 접속해보면 테스트로 업로드해본 이미지가 나온다
'PopcornMate' 카테고리의 다른 글
멀티 모듈 환경에서 내가 만든 api 테스트해보기 Feat. MockMvc, profileResolver, WithCustomUser (0) | 2024.03.24 |
---|---|
[Spring] FCM 구축하기 & FCM과 @Scheduled을 이용하여 특정 시간대에 알림 보내기 (4) | 2024.03.11 |
[리팩토링]InteliJ를 활용하여 멀티모듈 패키지 의존성 사이클 검사 및 개선 (1) | 2024.02.16 |
[Spring] 검색기능-QueryDsl로 무한스크롤로 넘겨주기 (0) | 2024.02.14 |
[인프라] 멀티모듈 적용기 (1) | 2024.02.08 |