Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] AWS S3 이미지 관련 기능 개발 #166

Merged
merged 5 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/test-task.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: Build test about test codes
name: Run gradlew clean test

on:
push:
branches: [ "refactor/162-file-system-structure" ] ## 테스트하고자하는 브랜치 작성
pull_request:
branches: [ "refactor/162-file-system-structure" ]
branches: [ "feat/161-aws-s3-image" ] ## 테스트하고자하는 브랜치 작성
# pull_request:
# branches: [ "feat/161-aws-s3-image" ]

jobs:
deploy:
Expand Down Expand Up @@ -46,5 +46,5 @@ jobs:
shell: bash

- name: Build and Test
run: ./gradlew clean build test
run: ./gradlew clean test

3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'

// AWS
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.1.RELEASE'

// json
implementation 'com.googlecode.json-simple:json-simple:1.1.1'

Expand Down
16 changes: 0 additions & 16 deletions src/main/java/com/genius/gitget/global/file/service/FileUtil.java
Original file line number Diff line number Diff line change
@@ -1,39 +1,23 @@
package com.genius.gitget.global.file.service;

import static com.genius.gitget.global.util.exception.ErrorCode.FILE_NOT_EXIST;
import static com.genius.gitget.global.util.exception.ErrorCode.IMAGE_NOT_ENCODED;
import static com.genius.gitget.global.util.exception.ErrorCode.NOT_SUPPORTED_EXTENSION;

import com.genius.gitget.global.file.domain.FileType;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.dto.CopyDTO;
import com.genius.gitget.global.file.dto.FileDTO;
import com.genius.gitget.global.util.exception.BusinessException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

@Component
public class FileUtil {
private final List<String> validExtensions = List.of("jpg", "jpeg", "png", "gif");

public static String encodedImage(Files files) {
try {
//TODO: local 환경에 종속된 메서드이므로 종속되지 않게 수정 필요
UrlResource urlResource = new UrlResource("file:" + files.getFileURI());

byte[] encode = Base64.getEncoder().encode(urlResource.getContentAsByteArray());
return new String(encode, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new BusinessException(IMAGE_NOT_ENCODED);
}
}

public FileDTO getFileDTO(MultipartFile file, FileType fileType, final String UPLOAD_PATH) {
String originalFilename = file.getOriginalFilename();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,91 @@
package com.genius.gitget.global.file.service;

import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.CopyObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.genius.gitget.global.file.domain.FileType;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.dto.CopyDTO;
import com.genius.gitget.global.file.dto.FileDTO;
import com.genius.gitget.global.file.dto.UpdateDTO;
import lombok.RequiredArgsConstructor;
import com.genius.gitget.global.util.exception.BusinessException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.springframework.core.io.UrlResource;
import org.springframework.web.multipart.MultipartFile;

/**
* !!!이 클래스의 모든 주석은 삭제해도 무방합니다!!!
* <p>
* S3 bucket에 이미지를 업로드하는 코드를 구현하는 곳
* 파일 시스템 구조 확장에 참고한 링크
* https://chb2005.tistory.com/200#3.5.%20%ED%8C%8C%EC%9D%BC%20%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C%20%EA%B5%AC%ED%98%84
* https://docs.aws.amazon.com/ko_kr/sdk-for-java/v1/developer-guide/examples-s3-objects.html#upload-object
*/
@RequiredArgsConstructor
public class S3FileManager implements FileManager {
private final AmazonS3 amazonS3;
private final FileUtil fileUtil;
private final String bucket;

public S3FileManager(AmazonS3 amazonS3, FileUtil fileUtil, String bucket) {
this.amazonS3 = amazonS3;
this.fileUtil = fileUtil;
this.bucket = bucket;
}

@Override
public String getEncodedImage(Files files) {
return null;
try {
UrlResource urlResource = new UrlResource(amazonS3.getUrl(bucket, files.getFileURI()));
byte[] encode = Base64.getEncoder().encode(urlResource.getContentAsByteArray());
return new String(encode, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new BusinessException(e);
}
}

@Override
public FileDTO upload(MultipartFile multipartFile, FileType fileType) {
return null;
try {
fileUtil.validateFile(multipartFile);
FileDTO fileDTO = fileUtil.getFileDTO(multipartFile, fileType, "");

ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(multipartFile.getSize());
objectMetadata.setContentType(multipartFile.getContentType());

amazonS3.putObject(bucket, fileDTO.fileURI(), multipartFile.getInputStream(), objectMetadata);
return fileDTO;
} catch (IOException e) {
throw new BusinessException(e);
}
}

@Override
public FileDTO copy(Files files, FileType fileType) {
return null;
CopyDTO copyDTO = fileUtil.getCopyInfo(files, fileType, "");

CopyObjectRequest copyObjectRequest = new CopyObjectRequest(
bucket, files.getFileURI(),
bucket, copyDTO.fileURI()
);
amazonS3.copyObject(copyObjectRequest);
return FileDTO.builder()
.fileType(fileType)
.originalFilename(copyDTO.originalFilename())
.savedFilename(copyDTO.savedFilename())
.fileURI(copyDTO.fileURI())
.build();
}

@Override
public UpdateDTO update(Files files, MultipartFile multipartFile) {
return null;
deleteInStorage(files);
FileDTO fileDTO = upload(multipartFile, files.getFileType());

return UpdateDTO.of(fileDTO);
}

@Override
public void deleteInStorage(Files files) {
try {
amazonS3.deleteObject(bucket, files.getFileURI());
} catch (SdkClientException e) {
throw new BusinessException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
import com.genius.gitget.global.util.formatter.LocalDateFormatter;
import com.genius.gitget.global.util.formatter.LocalDateTimeFormatter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.encrypt.AesBytesEncryptor;

@Slf4j
@Configuration
@RequiredArgsConstructor
public class AppConfig {
private final S3Config s3Config;
private final Environment env;


@Bean
public FileUtil fileUtil() {
return new FileUtil();
Expand All @@ -27,13 +27,15 @@ public FileUtil fileUtil() {
@Bean
public FileManager fileManager() {
final String fileMode = env.getProperty("file.mode");
final String UPLOAD_PATH = env.getProperty("file.upload.path");
assert fileMode != null;

if (fileMode.equals("local")) {
final String UPLOAD_PATH = env.getProperty("file.upload.path");
return new LocalFileManager(fileUtil(), UPLOAD_PATH);
}
return new S3FileManager(fileUtil());

final String bucket = env.getProperty("cloud.aws.s3.bucket");
return new S3FileManager(s3Config.amazonS3Client(), fileUtil(), bucket);
}

@Bean
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/genius/gitget/global/util/config/S3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.genius.gitget.global.util.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
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;

@Configuration
public class S3Config {
private final String accessKey;
private final String secretKey;
private final String region;

public S3Config(@Value("${cloud.aws.credentials.accessKey}") String accessKey,
@Value("${cloud.aws.credentials.secretKey}") String secretKey,
@Value("${cloud.aws.region.static}") String region) {
this.accessKey = accessKey;
this.secretKey = secretKey;
this.region = region;
}

@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@
import com.genius.gitget.challenge.instance.domain.Progress;
import com.genius.gitget.challenge.instance.repository.InstanceRepository;
import com.genius.gitget.challenge.user.domain.Role;
import com.genius.gitget.global.file.domain.FileType;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.service.FilesService;
import com.genius.gitget.util.TokenTestUtil;
import com.genius.gitget.util.WithMockCustomUser;
import com.genius.gitget.util.file.FileTestUtil;
import java.time.LocalDateTime;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -31,7 +28,6 @@
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartFile;

@SpringBootTest
@Transactional
Expand Down Expand Up @@ -145,9 +141,6 @@ public void setup() {


private Topic getSavedTopic() {
MultipartFile filename = FileTestUtil.getMultipartFile("sky");
Files files = filesService.uploadFile(filename, FileType.TOPIC);

Topic topic = topicRepository.save(
Topic.builder()
.title("title")
Expand All @@ -157,8 +150,6 @@ private Topic getSavedTopic() {
.pointPerPerson(100)
.build()
);
topic.setFiles(files);

return topic;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@
import com.genius.gitget.challenge.user.domain.Role;
import com.genius.gitget.challenge.user.domain.User;
import com.genius.gitget.challenge.user.repository.UserRepository;
import com.genius.gitget.global.file.domain.FileType;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.service.FilesService;
import com.genius.gitget.global.security.constants.ProviderInfo;
import com.genius.gitget.util.TokenTestUtil;
import com.genius.gitget.util.WithMockCustomUser;
import com.genius.gitget.util.file.FileTestUtil;
import java.time.LocalDateTime;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -40,7 +37,6 @@
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartFile;

@SpringBootTest
@Transactional
Expand Down Expand Up @@ -229,9 +225,6 @@ private User getSavedUser() {


private Topic getSavedTopic() {
MultipartFile filename = FileTestUtil.getMultipartFile("sky");
Files files = filesService.uploadFile(filename, FileType.TOPIC);

Topic topic = topicRepository.save(
Topic.builder()
.title("title")
Expand All @@ -241,8 +234,6 @@ private Topic getSavedTopic() {
.pointPerPerson(100)
.build()
);
topic.setFiles(files);

return topic;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,10 @@
import com.genius.gitget.challenge.user.domain.Role;
import com.genius.gitget.challenge.user.domain.User;
import com.genius.gitget.challenge.user.repository.UserRepository;
import com.genius.gitget.global.file.domain.FileType;
import com.genius.gitget.global.file.domain.Files;
import com.genius.gitget.global.file.service.FilesService;
import com.genius.gitget.global.security.constants.ProviderInfo;
import com.genius.gitget.util.TokenTestUtil;
import com.genius.gitget.util.WithMockCustomUser;
import com.genius.gitget.util.file.FileTestUtil;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -43,7 +40,6 @@
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartFile;


@SpringBootTest
Expand Down Expand Up @@ -278,9 +274,6 @@ private User getSavedUser() {
}

private Topic getSavedTopic() {
MultipartFile filename = FileTestUtil.getMultipartFile("sky");
Files files = filesService.uploadFile(filename, FileType.TOPIC);

Topic topic = topicRepository.save(
Topic.builder()
.title("title")
Expand All @@ -290,7 +283,6 @@ private Topic getSavedTopic() {
.pointPerPerson(100)
.build()
);
topic.setFiles(files);

return topic;
}
Expand Down