Skip to content

Commit f2b1c6d

Browse files
authored
Merge pull request #29 from Project-Catcher/feat-cw-apply-upload-s3
Feat: S3에 파일을 업로드 할 수 있도록 서비스 구현
2 parents 939d731 + 58287d6 commit f2b1c6d

File tree

13 files changed

+246
-6
lines changed

13 files changed

+246
-6
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ dependencies {
5555
//swagger
5656
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
5757

58+
//s3
59+
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
5860
}
5961

6062
dependencyManagement {

src/main/java/com/catcher/common/exception/BaseResponseStatus.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,18 @@ public enum BaseResponseStatus {
2222
// Common
2323
RESPONSE_ERROR(false, 3000, "값을 불러오는데 실패하였습니다."),
2424

25-
2625
/**
2726
* 4000 : Database, Server 오류
2827
*/
2928
DATABASE_ERROR(false, 4000, "데이터베이스 연결에 실패하였습니다."),
3029
REDIS_ERROR(false, 4002, "redis 연결에 실패하였습니다."),
3130

31+
/**
32+
* 5000: AWS Error
33+
*/
34+
S3UPLOAD_ERROR(false, 5000, "파일 업로드를 실패하였습니다."),
35+
KMS_ERROR(false, 5001, "암호화 및 복호화 과정에서 실패하였습니다."),
36+
AWS_IO_ERROR(false, 5002, "파일의 정보를 가져오는 데 실패했습니다.")
3237
;
3338

3439
private final boolean isSuccess;

src/main/java/com/catcher/core/domain/entity/BaseTimeEntity.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import lombok.Getter;
55
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
66

7+
import java.time.ZoneId;
78
import java.time.ZonedDateTime;
89

910
@EntityListeners(AuditingEntityListener.class)
1011
@MappedSuperclass
1112
@Getter
1213
public class BaseTimeEntity {
14+
protected static ZoneId ZONE = ZoneId.of("Asia/Seoul");
15+
1316
@Column(name = "created_at")
1417
private ZonedDateTime createdAt;
1518

@@ -18,12 +21,12 @@ public class BaseTimeEntity {
1821

1922
@PrePersist
2023
private void prePersist() {
21-
this.createdAt = ZonedDateTime.now();
22-
this.updatedAt = ZonedDateTime.now();
24+
this.createdAt = ZonedDateTime.now(ZONE);
25+
this.updatedAt = ZonedDateTime.now(ZONE);
2326
}
2427

2528
@PreUpdate
2629
private void preUpdate() {
27-
this.updatedAt = ZonedDateTime.now();
30+
this.updatedAt = ZonedDateTime.now(ZONE);
2831
}
2932
}

src/main/java/com/catcher/core/domain/entity/CatcherItem.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
@Getter
1212
@AllArgsConstructor(access = AccessLevel.PROTECTED)
1313
@NoArgsConstructor(access = AccessLevel.PROTECTED)
14-
@Where(clause = "deleted_at IS NULL")
1514
@Table(name = "catcher_item")
1615
public class CatcherItem extends BaseTimeEntity {
1716
@Id
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.catcher.infrastructure.external.service;
2+
3+
import com.amazonaws.services.kms.model.AWSKMSException;
4+
import com.amazonaws.services.s3.AmazonS3;
5+
import com.amazonaws.services.s3.model.AmazonS3Exception;
6+
import com.amazonaws.services.s3.model.ObjectMetadata;
7+
import com.catcher.common.exception.BaseException;
8+
import com.catcher.common.exception.BaseResponseStatus;
9+
import com.catcher.infrastructure.utils.KmsUtils;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.beans.factory.annotation.Value;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.web.multipart.MultipartFile;
14+
15+
import java.io.IOException;
16+
import java.util.UUID;
17+
18+
@Service
19+
@RequiredArgsConstructor
20+
public class S3UploadService {
21+
@Value("${cloud.aws.s3.bucket}")
22+
private String bucket;
23+
24+
private final AmazonS3 amazonS3;
25+
private final KmsUtils kmsUtils;
26+
27+
public String uploadFile(MultipartFile multipartFile) {
28+
String fileName = UUID.randomUUID().toString();
29+
30+
try {
31+
ObjectMetadata metadata = new ObjectMetadata();
32+
metadata.setContentLength(multipartFile.getSize());
33+
metadata.setContentType(multipartFile.getContentType());
34+
35+
String decryptedBucketName = kmsUtils.decrypt(bucket);
36+
amazonS3.putObject(decryptedBucketName, fileName, multipartFile.getInputStream(), metadata);
37+
38+
return amazonS3.getUrl(decryptedBucketName, fileName).toString();
39+
} catch (AWSKMSException awskmsException) {
40+
throw new BaseException(BaseResponseStatus.KMS_ERROR);
41+
} catch (AmazonS3Exception s3Exception) {
42+
throw new BaseException(BaseResponseStatus.S3UPLOAD_ERROR);
43+
} catch (IOException exception) {
44+
throw new BaseException(BaseResponseStatus.AWS_IO_ERROR);
45+
}
46+
}
47+
}

src/main/java/com/catcher/infrastructure/utils/KmsUtils.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.catcher.infrastructure.utils;
22

33
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
4-
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
54
import com.amazonaws.regions.Regions;
65
import com.amazonaws.services.kms.AWSKMS;
76
import com.amazonaws.services.kms.AWSKMSClientBuilder;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.catcher.infrastructure.utils;
2+
3+
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
4+
import com.amazonaws.regions.Regions;
5+
import com.amazonaws.services.s3.AmazonS3Client;
6+
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
10+
@Configuration
11+
public class S3Config {
12+
13+
@Bean
14+
public AmazonS3Client amazonS3Client(){
15+
return (AmazonS3Client) AmazonS3ClientBuilder
16+
.standard()
17+
.withRegion(Regions.AP_NORTHEAST_2)
18+
.withCredentials(DefaultAWSCredentialsProviderChain.getInstance())
19+
.build();
20+
}
21+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.catcher.resource;
2+
3+
import com.catcher.common.response.CommonResponse;
4+
import com.catcher.infrastructure.external.service.S3UploadService;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.http.MediaType;
7+
import org.springframework.web.bind.annotation.PostMapping;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RequestParam;
10+
import org.springframework.web.bind.annotation.RestController;
11+
import org.springframework.web.multipart.MultipartFile;
12+
13+
import java.io.IOException;
14+
15+
@RestController
16+
@RequestMapping("/s3")
17+
@RequiredArgsConstructor
18+
public class S3UploadController {
19+
20+
private final S3UploadService uploadService;
21+
22+
@PostMapping(path = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
23+
public CommonResponse<String> uploadFile(
24+
@RequestParam(value = "file") MultipartFile file
25+
) throws IOException {
26+
String savedUrl = uploadService.uploadFile(file);
27+
return CommonResponse.success(200, savedUrl);
28+
}
29+
}

src/main/resources/application-dev.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ ssh.local-port=3306
1515
aws.kms.keyId=5d1c783d-6c8e-4661-a8ed-a12654aac8c3
1616
aws.kms.encryptionAlgorithm=SYMMETRIC_DEFAULT
1717

18+
## s3
19+
cloud.aws.s3.bucket=AQICAHgxTvGwZVD/2MLMvR9/01Wy8IeL4nGHGGgc5XMYIhkQ2gHtX2l7w2o1cOCwj5+vyriHAAAAeDB2BgkqhkiG9w0BBwagaTBnAgEAMGIGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMgWN+1U9oZFD2pVW/AgEQgDUV7p6hnfU/Az/ZHSSJxg9O+Q7icBrADk1gdGuK6Mt+KlrHht5FdqHgXGPq8Av2fp71nENCmw==
20+
1821
## kakao
1922
kakao.api.key=AQICAHgxTvGwZVD/2MLMvR9/01Wy8IeL4nGHGGgc5XMYIhkQ2gHpQ9T3X8ABXTrlZfQaHCb6AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMzWswzD3/z9HQ1WWUAgEQgDtXvQXbxx434/7/6a/E7HS98MokawKTqdBkWY1eVid+ylY1AP38xkENY/RL6ER08Q4he8b6kZBL3iqq2w==
2023

src/main/resources/application-local.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ ssh.local-port=3306
1515
aws.kms.keyId=5d1c783d-6c8e-4661-a8ed-a12654aac8c3
1616
aws.kms.encryptionAlgorithm=SYMMETRIC_DEFAULT
1717

18+
## s3
19+
cloud.aws.s3.bucket=AQICAHgxTvGwZVD/2MLMvR9/01Wy8IeL4nGHGGgc5XMYIhkQ2gHtX2l7w2o1cOCwj5+vyriHAAAAeDB2BgkqhkiG9w0BBwagaTBnAgEAMGIGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMgWN+1U9oZFD2pVW/AgEQgDUV7p6hnfU/Az/ZHSSJxg9O+Q7icBrADk1gdGuK6Mt+KlrHht5FdqHgXGPq8Av2fp71nENCmw==
20+
1821
## kakao
1922
kakao.api.key=AQICAHgxTvGwZVD/2MLMvR9/01Wy8IeL4nGHGGgc5XMYIhkQ2gHpQ9T3X8ABXTrlZfQaHCb6AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMzWswzD3/z9HQ1WWUAgEQgDtXvQXbxx434/7/6a/E7HS98MokawKTqdBkWY1eVid+ylY1AP38xkENY/RL6ER08Q4he8b6kZBL3iqq2w==
2023

0 commit comments

Comments
 (0)