Skip to content

Commit

Permalink
[Feat] AWS S3 Pre-Signed Url 발급 API
Browse files Browse the repository at this point in the history
  • Loading branch information
isExample committed Sep 22, 2023
1 parent 25f1fd2 commit d3d35ff
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 2 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
implementation 'org.springframework.boot:spring-boot-starter-webflux' // WebClient
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' // S3
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
@SpringBootApplication
@EnableJpaAuditing
public class TransactionApplication {

static {
System.setProperty("com.amazonaws.sdk.disableEc2Metadata", "true"); // S3 예외발생 방지
}
public static void main(String[] args) {
SpringApplication.run(TransactionApplication.class, args);
}
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/HeyPorori/transaction/config/AmazonS3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package HeyPorori.transaction.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class AmazonS3Config {
@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
@Primary
public BasicAWSCredentials awsCredentialsProvider(){
return new BasicAWSCredentials(accessKey, secretKey);
}

@Bean
public AmazonS3 amazonS3Client() {
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentialsProvider()))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import HeyPorori.transaction.config.BaseResponse;
import HeyPorori.transaction.config.BaseResponseStatus;
import HeyPorori.transaction.dto.PostReq;
import HeyPorori.transaction.dto.PreSignedUrlRes;
import HeyPorori.transaction.service.AmazonS3Service;
import HeyPorori.transaction.service.TransactionService;
import HeyPorori.transaction.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
Expand All @@ -24,6 +25,7 @@
public class TransactionController {
private final TransactionService transactionService;
private final UserService userService;
private final AmazonS3Service amazonS3Service;

// 테스트용 APIs
@Operation(summary = "Swagger UI 테스트용 메서드", description = "프로젝트 초기 Swagger UI 정상작동을 확인하기 위한 메서드입니다.")
Expand All @@ -47,4 +49,11 @@ public BaseResponse<BaseResponseStatus> createPost(@RequestHeader("Authorization
transactionService.createPost(token, postReq);
return new BaseResponse<>(BaseResponseStatus.SUCCESS);
}

@Operation(summary = "Pre-Signed Url 발급 API", description = "AWS S3 이미지 업로드 권한을 요청하기 위한 API입니다.")
@ApiResponse(responseCode = "200", description = "요청 성공", content = @Content(mediaType = "application/json", schema = @Schema(implementation = BaseResponse.class)))
@GetMapping("/url")
public BaseResponse<PreSignedUrlRes> getPreSignedUrl(@RequestHeader("Authorization") String token) throws BaseException {
return new BaseResponse<>(amazonS3Service.getPreSignedUrl());
}
}
14 changes: 14 additions & 0 deletions src/main/java/HeyPorori/transaction/dto/PreSignedUrlRes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package HeyPorori.transaction.dto;

import lombok.Builder;
import lombok.Getter;

@Getter
public class PreSignedUrlRes {
private String url;

@Builder
public PreSignedUrlRes(String url) {
this.url = url;
}
}
55 changes: 55 additions & 0 deletions src/main/java/HeyPorori/transaction/service/AmazonS3Service.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package HeyPorori.transaction.service;

import HeyPorori.transaction.dto.PreSignedUrlRes;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.Headers;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.Date;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Transactional
public class AmazonS3Service {
private final AmazonS3 amazonS3;

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

public PreSignedUrlRes getPreSignedUrl(){
String uuid = UUID.randomUUID().toString();
String objectKey = "transactions/"+uuid;

GeneratePresignedUrlRequest request = generatePresignedUrlRequest(bucket, objectKey);
PreSignedUrlRes response = PreSignedUrlRes.builder()
.url(amazonS3.generatePresignedUrl(request).toString())
.build();
return response;
}

private GeneratePresignedUrlRequest generatePresignedUrlRequest(String bucket, String imageName){
Date expiration = new Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += 1000 * 60 * 5; // 5분
expiration.setTime(expTimeMillis);

//Pre-Signed Url request 생성
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, imageName)
.withMethod(HttpMethod.PUT)
.withExpiration(expiration);

//request 파라미터 추가
request.addRequestParameter(
Headers.S3_CANNED_ACL,
CannedAccessControlList.PublicRead.toString());

return request;
}
}

0 comments on commit d3d35ff

Please sign in to comment.