Skip to content

Commit

Permalink
feat: presigned URL 이미지 저장 로직 변경 (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondanythings authored Jul 29, 2024
1 parent 70a5f7e commit d1e7c44
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ public interface ExternalApi {
@Operation(summary = "Presigned URL 발급받기",
method = "POST", description = """
이미지 업로드를 위한 Presigned URL 발급합니다.
- GetPreSignedURLResponse.presignedUrl: 이미지 저장을 위한 요청 URL<br />
- GetPreSignedURLResponse.imageUrl: 저장 완료된 이미지 요청 URL
# 참고사항
presigned URL 요청 시 헤더에 담긴 Content-type에 따라 파일의 확장자가 정해집니다.
사용처에 따라 정확한 Content-type으로 요청 부탁드립니다.
"""
)
@ApiResponses({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.layer.domain.external.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.layer.common.annotation.MemberId;
import org.layer.domain.external.api.ExternalApi;
import org.layer.domain.external.dto.ExternalRequest;
import org.layer.domain.external.dto.ExternalResponse;
import org.layer.external.ncp.dto.NcpResponse;
import org.layer.external.ncp.service.NcpService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -14,6 +16,7 @@

@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/external")
public class ExternalController implements ExternalApi {

Expand All @@ -23,8 +26,9 @@ public class ExternalController implements ExternalApi {
@GetMapping("/image/presigned")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<ExternalResponse.GetPreSignedURLResponse> getPresignedURL(@MemberId Long memberId, ExternalRequest.GetPreSignedURLRequest getPreSignedURLRequest) {
String url = ncpService.getPreSignedUrl(memberId, getPreSignedURLRequest.domain());
NcpResponse.PresignedResult presignedResult = ncpService.getPreSignedUrl(memberId, getPreSignedURLRequest.domain());

return ResponseEntity.ok(ExternalResponse.GetPreSignedURLResponse.toResponse(url));

return ResponseEntity.ok(ExternalResponse.GetPreSignedURLResponse.toResponse(presignedResult.presignedUrl(), presignedResult.imageUrl()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@ public class ExternalResponse {
@Builder
@Schema(description = "Presigned URL 응답")
public record GetPreSignedURLResponse(
@Schema(description = "생성된 Presigned URL")
@Schema(title = "생성된 Presigned URL", description = """
이미지 저장을 위한 요청 URL<br />
*요청시 보낸 Content-type에 따라 확장자가 결정됩니다*
""")
@NotNull
String url
String presignedUrl,

@Schema(title = "업르드된 이미지 주소", description = """
도메인 별로 엔티티 생성 시 해당 파라미터 객체 유무 확인 로직이 있습니다.
업로드 성공 이후 엔티티 생성 요청 부탁드립니다
""")
String imageUrl
) {
public static GetPreSignedURLResponse toResponse(String url) {
return Optional.ofNullable(url).map(it -> GetPreSignedURLResponse.builder().url(url).build()).orElseThrow(() -> new ExternalExeption(INTERNAL_SERVER_ERROR));
public static GetPreSignedURLResponse toResponse(String presignedUrl, String imageUrl) {
return Optional.ofNullable(presignedUrl)
.map(it -> GetPreSignedURLResponse.builder()
.presignedUrl(presignedUrl)
.imageUrl(imageUrl)
.build()
).orElseThrow(() -> new ExternalExeption(INTERNAL_SERVER_ERROR));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.layer.external.ncp.dto;

import lombok.Builder;

public class NcpResponse {

@Builder
public record PresignedResult(
String presignedUrl,
String imageUrl
) {

public static PresignedResult toResponse(String presignedUrl,
String imageUrl) {
return PresignedResult.builder().presignedUrl(presignedUrl).imageUrl(imageUrl).build();

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.layer.external.ncp.dto.NcpResponse;
import org.layer.external.ncp.enums.ImageDomain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand All @@ -24,12 +25,16 @@ public class NcpService {

private final AmazonS3Client amazonS3Client;

public String getPreSignedUrl(Long memberId, ImageDomain imageDomain) {
String fileName = imageDomain + "/" + memberId.toString() + "/" + UUID.randomUUID();
public NcpResponse.PresignedResult getPreSignedUrl(Long memberId, ImageDomain imageDomain) {
String imagePath = imageDomain + "/" + memberId.toString() + "/" + UUID.randomUUID();
var imageUrl = amazonS3Client.getUrl(bucket, imagePath);

GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(bucket, fileName);
GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(bucket, imagePath);

return amazonS3Client.generatePresignedUrl(generatePresignedUrlRequest).toString();
return NcpResponse.PresignedResult.toResponse(
amazonS3Client.generatePresignedUrl(generatePresignedUrlRequest).toString(),
imageUrl.toString()
);
}

private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String bucket, String fileName) {
Expand All @@ -46,7 +51,9 @@ private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String bucket
private Date getPreSignedUrlExpiration() {
Date expiration = new Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += 1000 * 60 * 2;

// 15 초
expTimeMillis += 1000 * 15;
expiration.setTime(expTimeMillis);
return expiration;
}
Expand Down

0 comments on commit d1e7c44

Please sign in to comment.