-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: s3에 의존적이지 않도록 이미지 업로드 추상화 (#658)
* refactor: StorageService 추상화 * refactor: s3 path 위치를 application.yml 파일에 설정할 수 있도록 변경 * refactor: s3버킷 설정을 AmazonS3StorageService가 가져가도록 변경 * feat: 로컬에 이미지를 저장할 수 있는 기능 구현 * refactor: lombok 어노테이션 변경 * refactor: 이미지 업로드 메서드 시그니처 번경
- Loading branch information
Showing
12 changed files
with
180 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
backend/src/main/java/com/zzang/chongdae/storage/service/AmazonS3StorageService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package com.zzang.chongdae.storage.service; | ||
|
||
import com.amazonaws.SdkClientException; | ||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.model.ObjectMetadata; | ||
import com.amazonaws.services.s3.model.PutObjectRequest; | ||
import com.zzang.chongdae.global.exception.MarketException; | ||
import com.zzang.chongdae.storage.exception.StorageErrorCode; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.UUID; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.web.multipart.MultipartFile; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
@RequiredArgsConstructor | ||
public class AmazonS3StorageService implements StorageService { | ||
|
||
private final AmazonS3 s3Client; | ||
|
||
@Value("${amazon.s3.bucket}") | ||
private String bucketName; | ||
|
||
@Value("${amazon.cloudfront.redirectUrl}") | ||
private String redirectUrl; | ||
|
||
@Value("${amazon.cloudfront.storagePath}") | ||
private String storagePath; | ||
|
||
@Override | ||
public String uploadFile(MultipartFile file) { | ||
try { | ||
String objectKey = storagePath + UUID.randomUUID(); | ||
InputStream inputStream = file.getInputStream(); | ||
ObjectMetadata metadata = createMetadata(file); | ||
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, inputStream, metadata); | ||
s3Client.putObject(putObjectRequest); | ||
return createUri(objectKey); | ||
} catch (IOException e) { | ||
throw new MarketException(StorageErrorCode.INVALID_FILE); | ||
} catch (SdkClientException e) { | ||
throw new MarketException(StorageErrorCode.STORAGE_SERVER_FAIL); | ||
} | ||
} | ||
|
||
private ObjectMetadata createMetadata(MultipartFile file) { | ||
ObjectMetadata metadata = new ObjectMetadata(); | ||
metadata.setContentLength(file.getSize()); | ||
metadata.setContentType(file.getContentType()); | ||
return metadata; | ||
} | ||
|
||
private String createUri(String objectKey) { | ||
return UriComponentsBuilder.newInstance() | ||
.scheme("https") | ||
.host(redirectUrl) | ||
.path("/" + objectKey) | ||
.build(false) | ||
.toString(); | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
backend/src/main/java/com/zzang/chongdae/storage/service/LocalStorageService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.zzang.chongdae.storage.service; | ||
|
||
import com.zzang.chongdae.global.exception.MarketException; | ||
import com.zzang.chongdae.storage.exception.StorageErrorCode; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.nio.file.StandardCopyOption; | ||
import java.util.Set; | ||
import java.util.UUID; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.web.multipart.MultipartFile; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
@RequiredArgsConstructor | ||
public class LocalStorageService implements StorageService { | ||
|
||
private static final Set<String> ALLOW_IMAGE_EXTENSIONS = Set.of("jpg", "jpeg", "png", "gif", "bmp", "svg"); | ||
|
||
@Value("${storage.redirectUrl}") | ||
private String redirectUrl; | ||
|
||
@Value("${storage.path}") | ||
private String storagePath; | ||
|
||
@Override | ||
public String uploadFile(MultipartFile file) { | ||
try { | ||
String extension = getFileExtension(file); | ||
validateFileExtension(extension); | ||
String newFilename = UUID.randomUUID() + "." + extension; | ||
Path uploadPath = Paths.get(storagePath); | ||
Path filePath = uploadPath.resolve(newFilename); | ||
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); | ||
return createUri(filePath.toString()); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
|
||
private String getFileExtension(MultipartFile file) { | ||
String originalFilename = file.getOriginalFilename(); | ||
if (originalFilename == null || !originalFilename.contains(".")) { | ||
throw new MarketException(StorageErrorCode.INVALID_FILE); | ||
} | ||
return originalFilename.substring(originalFilename.lastIndexOf('.') + 1).toLowerCase(); | ||
} | ||
|
||
private void validateFileExtension(String extension) { | ||
if (!ALLOW_IMAGE_EXTENSIONS.contains(extension)) { | ||
throw new MarketException(StorageErrorCode.INVALID_FILE_EXTENSION); | ||
} | ||
} | ||
|
||
private String createUri(String objectKey) { | ||
return UriComponentsBuilder.newInstance() | ||
.scheme("https") | ||
.host(redirectUrl) | ||
.path("/" + objectKey) | ||
.build(false) | ||
.toString(); | ||
} | ||
} |
56 changes: 2 additions & 54 deletions
56
backend/src/main/java/com/zzang/chongdae/storage/service/StorageService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,8 @@ | ||
package com.zzang.chongdae.storage.service; | ||
|
||
import com.amazonaws.SdkClientException; | ||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.model.ObjectMetadata; | ||
import com.amazonaws.services.s3.model.PutObjectRequest; | ||
import com.zzang.chongdae.global.exception.MarketException; | ||
import com.zzang.chongdae.storage.exception.StorageErrorCode; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.UUID; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.multipart.MultipartFile; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class StorageService { | ||
public interface StorageService { | ||
|
||
private final AmazonS3 s3Client; | ||
|
||
@Value("${amazon.s3.bucket}") | ||
private String bucketName; | ||
|
||
@Value("${amazon.cloudfront.redirectUrl}") | ||
private String redirectUrl; | ||
|
||
public String uploadFile(MultipartFile file, String path) { | ||
try { | ||
String objectKey = path + UUID.randomUUID(); | ||
InputStream inputStream = file.getInputStream(); | ||
ObjectMetadata metadata = createMetadata(file); | ||
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectKey, inputStream, metadata); | ||
s3Client.putObject(putObjectRequest); | ||
return createUri(objectKey); | ||
} catch (IOException e) { | ||
throw new MarketException(StorageErrorCode.INVALID_FILE); | ||
} catch (SdkClientException e) { | ||
throw new MarketException(StorageErrorCode.STORAGE_SERVER_FAIL); | ||
} | ||
} | ||
|
||
private ObjectMetadata createMetadata(MultipartFile file) { | ||
ObjectMetadata metadata = new ObjectMetadata(); | ||
metadata.setContentLength(file.getSize()); | ||
metadata.setContentType(file.getContentType()); | ||
return metadata; | ||
} | ||
|
||
private String createUri(String objectKey) { | ||
return UriComponentsBuilder.newInstance() | ||
.scheme("https") | ||
.host(redirectUrl) | ||
.path("/" + objectKey) | ||
.build(false) | ||
.toString(); | ||
} | ||
String uploadFile(MultipartFile file); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
backend/src/test/java/com/zzang/chongdae/offering/config/TestStorageConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.zzang.chongdae.offering.config; | ||
|
||
import com.zzang.chongdae.offering.util.FakeStorageService; | ||
import com.zzang.chongdae.storage.service.StorageService; | ||
import org.springframework.boot.test.context.TestConfiguration; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Primary; | ||
|
||
@TestConfiguration | ||
public class TestStorageConfig { | ||
|
||
@Bean | ||
@Primary | ||
public StorageService testStorageService() { | ||
return new FakeStorageService(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
backend/src/test/java/com/zzang/chongdae/offering/util/FakeStorageService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.zzang.chongdae.offering.util; | ||
|
||
import com.zzang.chongdae.storage.service.StorageService; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
public class FakeStorageService implements StorageService { | ||
|
||
@Override | ||
public String uploadFile(MultipartFile file) { | ||
return "https://upload-image-url.com/"; | ||
} | ||
} |