Skip to content

Commit

Permalink
Merge pull request #25 from Food-GO/feature/#21
Browse files Browse the repository at this point in the history
S3 change to presigned url
  • Loading branch information
Jeongh00 authored Oct 3, 2024
2 parents 08079ee + 2f711cd commit 2a85c37
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 22 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Backend CD # actions 이름

on:
push:
branches: [ feature/#19 ]
branches: [ feature/#21 ]

jobs:
deploy:
Expand Down Expand Up @@ -45,12 +45,12 @@ jobs:
run: ./gradlew bootJar

- name: 도커 이미지 빌드
run: sudo docker build -t ${{ secrets.DOCKER_IMG }} --platform linux/amd64 .
run: sudo docker build -t ${{ secrets.DOCKER_IMG_SPRING }} --platform linux/amd64 .

- name: 도커 이미지 push
run: |
sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
sudo docker push ${{ secrets.DOCKER_IMG }}
sudo docker push ${{ secrets.DOCKER_IMG_SPRING }}
- name: scp file
uses: appleboy/scp-action@master
Expand All @@ -73,6 +73,7 @@ jobs:
echo "${{ secrets.DOCKER_COMPOSE }}" > ./docker-compose.yml
sudo docker stop $(sudo docker ps -a -q)
sudo docker rm -f $(sudo docker ps -qa)
sudo docker pull ${{ secrets.DOCKER_IMG }}
sudo docker pull ${{ secrets.DOCKER_IMG_SPRING }}
sudo docker pull ${{ secrets.DOCKER_IMG_FLASK }}
sudo docker-compose -f docker-compose.yml up -d
sudo docker image prune -f
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.foodgo.apimodule.cuisine.dto.TestResultType;
import com.foodgo.apimodule.ingredient.dto.IngredientInfo;
import com.foodgo.apimodule.ingredient.mapper.IngredientMapper;
import com.foodgo.commonmodule.image.service.FileService;
import com.foodgo.coremodule.cuisine.domain.Ingredient;
import com.foodgo.coremodule.cuisine.domain.TestType;
import com.foodgo.coremodule.cuisine.exception.CuisineErrorCode;
Expand All @@ -25,6 +26,7 @@
public class CuisineFindUseCase {

private final CuisineQueryService cuisineQueryService;
private final FileService fileService;

@Value("${spring.openapi.key.recipe}")
private String apiKey;
Expand All @@ -35,7 +37,8 @@ public List<IngredientInfo> findIngredientInfo(User user) {

List<Ingredient> ingredients = cuisineQueryService.findIngredientsByUserId(user.getId());
for (Ingredient ingredient : ingredients) {
final IngredientInfo infoDTO = IngredientMapper.toInfoDTO(ingredient);
String imageUrl = ingredient.getImageUrl().isEmpty() ? null : fileService.getDownloadPresignedUrl(ingredient.getImageUrl());
final IngredientInfo infoDTO = IngredientMapper.toInfoDTO(ingredient, imageUrl);
infoList.add(infoDTO);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
package com.foodgo.apimodule.cuisine.application;

import com.foodgo.apimodule.ingredient.dto.IngredientAddReq;
import com.foodgo.apimodule.cuisine.dto.TestResultType;
import com.foodgo.apimodule.ingredient.dto.IngredientAddReq;
import com.foodgo.apimodule.ingredient.mapper.IngredientMapper;
import com.foodgo.commonmodule.image.service.AwsS3Service;
import com.foodgo.coremodule.cuisine.domain.CuisineTest;
import com.foodgo.coremodule.cuisine.domain.Ingredient;
import com.foodgo.coremodule.cuisine.service.CuisineQueryService;
import com.foodgo.coremodule.user.domain.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
@RequiredArgsConstructor
@Transactional
public class CuisineSaveUseCase {

private final CuisineQueryService cuisineQueryService;
private final AwsS3Service awsS3Service;

public void saveIngredient(IngredientAddReq addReq, MultipartFile multipartFile, User user) {

String imageUrl = awsS3Service.uploadFile(multipartFile);
Ingredient ingredient = IngredientMapper.toIngredientEntity(addReq, user, imageUrl);
public void saveIngredient(IngredientAddReq addReq, User user) {

Ingredient ingredient = IngredientMapper.toIngredientEntity(addReq, user);
cuisineQueryService.saveIngredient(ingredient);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.net.URISyntaxException;
import java.util.List;
Expand Down Expand Up @@ -62,10 +61,9 @@ public ApplicationResponse<List<IngredientInfo>> findIngredientList(@UserResolve
)
@Operation(summary = "식재료 리스트 추가 API", description = "식재료 리스트 추가 API 입니다.")
public ApplicationResponse<String> addIngredientList(@UserResolver User user,
@RequestPart(value = "dto") IngredientAddReq addReq,
@RequestPart(value = "file") MultipartFile multipartFile) {
@RequestBody IngredientAddReq addReq) {

cuisineSaveUseCase.saveIngredient(addReq, multipartFile, user);
cuisineSaveUseCase.saveIngredient(addReq, user);
return ApplicationResponse.onSuccess("식재료 리스트 추가되었습니다.");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.foodgo.apimodule.file.presentation;

import com.foodgo.commonmodule.common.ApplicationResponse;
import com.foodgo.commonmodule.image.dto.PresignedUrlResponse;
import com.foodgo.commonmodule.image.service.FileService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "파일 관리 API", description = "파일 업로드 및 다운로드 관련 API")
@RestController
@RequestMapping("/api/v1/files")
@RequiredArgsConstructor
public class FileController {

private final FileService fileService;

@Operation(summary = "프리사인드 URL 생성", description = "파일 업로드를 위한 프리사인드 URL을 생성합니다. 유효기간 15분 입니다.")
@GetMapping("/presigned-url")
public ApplicationResponse<PresignedUrlResponse> getPresignedUrl(
@Parameter(description = "파일 경로의 prefix (이미지의 경우 images 필수)", example = "/images") @RequestParam(name = "prefix", defaultValue = "images") String prefix,
@Parameter(description = "파일 이름", required = true, example = "example.txt") @RequestParam(name = "fileName") String fileName) {
return ApplicationResponse.onSuccess(fileService.getUploadPresignedUrl(prefix, fileName));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

public record IngredientAddReq(
String name,
String quantity
String quantity,
String imageUrl
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IngredientMapper {

public static IngredientInfo toInfoDTO(Ingredient ingredient) {
public static IngredientInfo toInfoDTO(Ingredient ingredient, String imageUrl) {
return new IngredientInfo(
ingredient.getId(),
ingredient.getName(),
ingredient.getQuantity(),
ingredient.getImageUrl()
imageUrl
);
}

public static Ingredient toIngredientEntity(IngredientAddReq addReq, User user, String imageUrl) {
public static Ingredient toIngredientEntity(IngredientAddReq addReq, User user) {
return Ingredient.builder()
.name(addReq.name())
.quantity(addReq.quantity())
.imageUrl(imageUrl)
.imageUrl(addReq.imageUrl())
.user(user)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.foodgo.commonmodule.image.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
@Schema(description = "프리사인드 URL 응답 DTO")
public class PresignedUrlResponse {

@Schema(description = "생성된 프리사인드 URL", example = "https://example.com/presigned-url")
private final String url;

@Schema(description = "파일 경로", example = "images/example.txt")
private final String filePath;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.foodgo.commonmodule.image.service;

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.foodgo.commonmodule.image.dto.PresignedUrlResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class FileService {

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

@Value("${cloud.aws.s3.expTime}")
private Long expTime;


private final AmazonS3 amazonS3;

public PresignedUrlResponse getUploadPresignedUrl(String prefix, String originalFileName) {
String filePath = createPath(prefix, originalFileName);
GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePresignedUrlRequest(bucket, filePath, HttpMethod.PUT);
URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest);

return new PresignedUrlResponse(url.toString(), filePath);
}

public String getDownloadPresignedUrl(String filePath) {
if (filePath != null && filePath.startsWith("images/")) {
GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePresignedUrlRequest(bucket, filePath, HttpMethod.GET);
URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest);
return url.toString();
}

return filePath;
}

private GeneratePresignedUrlRequest getGeneratePresignedUrlRequest(String bucket, String fileName, HttpMethod method) {
GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, fileName)
.withMethod(method)
.withExpiration(getPresignedUrlExpiration());

return generatePresignedUrlRequest;
}

private Date getPresignedUrlExpiration() {
Date expiration = new Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += expTime;
expiration.setTime(expTimeMillis);

return expiration;
}

private String createFileId() {
return UUID.randomUUID().toString();
}

private String createPath(String prefix, String fileName) {
String fileId = createFileId();
String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
return String.format("%s/%s-%s-%s", prefix, timestamp, fileId, fileName);
}
}

0 comments on commit 2a85c37

Please sign in to comment.