-
Notifications
You must be signed in to change notification settings - Fork 0
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/#464 이미지 s3 도입 #479
Merged
The head ref may contain hidden characters: "feat/#464_\uC774\uBBF8\uC9C0_s3_\uB3C4\uC785"
Merged
Feat/#464 이미지 s3 도입 #479
Changes from 14 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
1d873ec
feat: 이미지를 저장할 수 있는 ImageService 및 ImageClient 생성
hectick 5a0865b
refactor: 로컬 환경에서 이미지를 저장, 삭제할 때 ImageFileUploader 대신 LocalImageClien…
hectick cddb4c9
refactor: 게시글, 댓글 조회시 이미지 url을 Domain을 통해서가 아니라 ImageService를 통해서 알아내…
hectick 2e7fa41
refactor: properties 파일의 domain 변수명을 image.domain으로 구체화 및 테스트 코드 추가
hectick 7b00a10
fix: 이미지 삭제 버그 수정
hectick 8a2247c
refactor: 메서드명 수정
hectick 947af09
feat: prod 환경에서 s3에 이미지를 저장하는 기능 구현
hectick 36efbbc
Merge remote-tracking branch 'origin/dev' into feat/#464_이미지_s3_도입
hectick 970d320
refactor: application-prod.properties 다듬기
hectick 9fb67b8
fix: S3Client configuration
hectick 2743167
fix: final 제거
hectick da9f56f
fix: contentType 지정
hectick 39a5b0f
refactor: 주석 제거
hectick ea1f06b
feat: 이미지 s3로 이주하는 api 생성
hectick 69b8b73
Merge remote-tracking branch 'origin/dev' into feat/#464_이미지_s3_도입
hectick ebe422d
fix: 머지 후 애플리케이션 동작하도록 코드 수정
hectick 55e18df
refactor:
hectick 9e46be3
Merge remote-tracking branch 'origin/dev' into feat/#464_이미지_s3_도입
hectick 2e05fb6
refactor: 어노테이션 순서 변경
hectick e37c0fe
docs: 회원정보수정 문서화 수정
hectick ed721e0
refactor: 변수명 변경
hectick bef925f
refactor: 캡슐화
hectick 1880dea
refactor: ImageInfoFactory 클래스 분리 및 image 관련 클래스들 패키지 변경
hectick de6231a
refactor: 오래된 파일 경로 원상복구
hectick b435108
feat: 프로필 자동 삭제 기능 도입
hectick d7cda0b
Merge remote-tracking branch 'origin/dev' into feat/#464_이미지_s3_도입
hectick File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
20 changes: 20 additions & 0 deletions
20
backend/src/main/java/edonymyeon/backend/global/config/S3Config.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,20 @@ | ||
package edonymyeon.backend.global.config; | ||
|
||
import static software.amazon.awssdk.regions.Region.AP_NORTHEAST_2; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; | ||
import software.amazon.awssdk.services.s3.S3Client; | ||
|
||
@Configuration | ||
public class S3Config { | ||
|
||
@Bean | ||
public S3Client s3Client() { | ||
return S3Client.builder() | ||
.credentialsProvider(InstanceProfileCredentialsProvider.builder().build()) | ||
.region(AP_NORTHEAST_2) | ||
.build(); | ||
} | ||
} |
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
66 changes: 0 additions & 66 deletions
66
backend/src/main/java/edonymyeon/backend/image/ImageFileUploader.java
This file was deleted.
Oops, something went wrong.
12 changes: 12 additions & 0 deletions
12
backend/src/main/java/edonymyeon/backend/image/application/ImageClient.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 edonymyeon.backend.image.application; | ||
|
||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
public interface ImageClient { | ||
|
||
void upload(final MultipartFile image, final String directory, final String storeName); | ||
|
||
boolean supportsDeletion(); | ||
|
||
void delete(final String imagePath); | ||
} |
8 changes: 8 additions & 0 deletions
8
backend/src/main/java/edonymyeon/backend/image/application/ImageMigrationClient.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,8 @@ | ||
package edonymyeon.backend.image.application; | ||
|
||
import java.io.File; | ||
|
||
public interface ImageMigrationClient { | ||
|
||
void migrate(final File image, final String directory, final String storeName); | ||
} |
77 changes: 77 additions & 0 deletions
77
backend/src/main/java/edonymyeon/backend/image/application/ImageMigrationService.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,77 @@ | ||
package edonymyeon.backend.image.application; | ||
|
||
import static edonymyeon.backend.global.exception.ExceptionInformation.IMAGE_ORIGINAL_DIRECTORY_INVALID; | ||
import static java.util.Objects.isNull; | ||
|
||
import edonymyeon.backend.global.exception.BusinessLogicException; | ||
import edonymyeon.backend.image.commentimage.domain.CommentImageInfo; | ||
import edonymyeon.backend.image.commentimage.repository.CommentImageInfoRepository; | ||
import edonymyeon.backend.image.postimage.domain.PostImageInfo; | ||
import edonymyeon.backend.image.postimage.repository.PostImageInfoRepository; | ||
import java.io.File; | ||
import java.io.FilenameFilter; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class ImageMigrationService { | ||
|
||
private final PostImageInfoRepository postImageInfoRepository; | ||
|
||
private final CommentImageInfoRepository commentImageInfoRepository; | ||
|
||
private final ImageMigrationClient imageMigrationClient; | ||
|
||
@Value("${image.root-dir}") | ||
private String newDir; | ||
|
||
@Value("${file.dir}") | ||
private String originalDir; | ||
|
||
public void migrate() { | ||
File[] files = new File(originalDir).listFiles(getFilter()); | ||
if (isNull(files)) { | ||
throw new BusinessLogicException(IMAGE_ORIGINAL_DIRECTORY_INVALID); | ||
} | ||
|
||
final List<PostImageInfo> postImages = postImageInfoRepository.findAllImages(); | ||
final List<CommentImageInfo> commentImages = commentImageInfoRepository.findAllImages(); | ||
//todo: 프로필 이미지 꺼내오기 | ||
|
||
for (File file : files) { | ||
final String name = file.getName(); | ||
// 게시글 이미지인지 판단 | ||
if (isPostImage(postImages, name)) { | ||
imageMigrationClient.migrate(file, newDir + ImageType.POST.getSaveDirectory(), name); | ||
} | ||
// 댓글 이미지인지 판단 | ||
if (isCommentImage(commentImages, name)) { | ||
imageMigrationClient.migrate(file, newDir + ImageType.COMMENT.getSaveDirectory(), name); | ||
} | ||
// todo: 프로필 이미지라면 -> cloudfront의 profile 폴더로 이동 | ||
// todo: 어디에도 해당되지 않는 경우(db에 정보가 없는!) -> 삭제 | ||
} | ||
} | ||
|
||
@NotNull | ||
private FilenameFilter getFilter() { | ||
return new FilenameFilter() { | ||
@Override | ||
public boolean accept(final File dir, final String name) { | ||
return !name.equals("edonymyeon-firebase.json"); | ||
} | ||
}; | ||
} | ||
|
||
private boolean isPostImage(final List<PostImageInfo> postImages, final String name) { | ||
return postImages.stream().anyMatch(each -> each.getStoreName().equals(name)); | ||
} | ||
|
||
private boolean isCommentImage(final List<CommentImageInfo> commentImages, final String name) { | ||
return commentImages.stream().anyMatch(each -> each.getStoreName().equals(name)); | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
backend/src/main/java/edonymyeon/backend/image/application/ImageService.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,96 @@ | ||
package edonymyeon.backend.image.application; | ||
|
||
import static edonymyeon.backend.global.exception.ExceptionInformation.IMAGE_EXTENSION_INVALID; | ||
|
||
import edonymyeon.backend.global.exception.EdonymyeonException; | ||
import edonymyeon.backend.image.ImageExtension; | ||
import edonymyeon.backend.image.ImageFileNameStrategy; | ||
import edonymyeon.backend.image.domain.Domain; | ||
import edonymyeon.backend.image.domain.ImageInfo; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
This2sho marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public class ImageService { | ||
This2sho marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private final ImageClient imageClient; | ||
|
||
private final ImageFileNameStrategy imageFileNameStrategy; | ||
|
||
private final Domain domain; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이미지 서비스가 생기면서 각각 서비스에서 사용하던 |
||
|
||
@Value("${image.root-dir}") | ||
private String rootDirectory; //todo: 어디다 두는게 좋을까? | ||
|
||
public ImageInfo save(final MultipartFile image, final ImageType imageType) { | ||
final String originalFileName = image.getOriginalFilename(); | ||
validateExtension(originalFileName); | ||
final String storeName = imageFileNameStrategy.createName(originalFileName); | ||
final ImageInfo imageInfo = new ImageInfo(storeName); //todo: imageType에 따라 imageInfo 변환 | ||
imageClient.upload( | ||
image, | ||
rootDirectory + imageType.getSaveDirectory(), | ||
storeName | ||
); | ||
return imageInfo; //todo: 생성된 imageInfo 저장 및 반환 | ||
} | ||
|
||
public List<ImageInfo> saveAll(final List<MultipartFile> images, final ImageType imageType) { | ||
return images.stream() | ||
.map(each -> save(each, imageType)) | ||
.toList(); | ||
} | ||
|
||
private void validateExtension(final String originalFileName) { | ||
final String ext = ImageExtension.extractExt(originalFileName); | ||
if (ImageExtension.contains(ext)) { | ||
return; | ||
} | ||
throw new EdonymyeonException(IMAGE_EXTENSION_INVALID); | ||
} | ||
|
||
/** | ||
* @param storeName 이미지의 이름 | ||
* @param imageType 이미지 사용처 | ||
* @return 리소스가 저장된 실제 경로 | ||
*/ | ||
private String findResourcePath(final String storeName, final ImageType imageType) { | ||
return rootDirectory + imageType.getSaveDirectory() + storeName; | ||
} | ||
|
||
/** | ||
* @param fileName 예를 들면 post/name.png 처럼 이미지 타입까지 같이 들어온다 | ||
* @return 리소스가 저장된 실제 경로 | ||
*/ | ||
public String findResourcePath(final String fileName) { | ||
return rootDirectory + fileName; | ||
} | ||
|
||
/** | ||
* @param imageType 이미지 사용처 | ||
* @return 사용자에게 보여줄 이미지 도메인 주소(단 이미지 파일 이름은 제외, 예를 들어 https://localhost:8080/images/post/) | ||
*/ | ||
public String findBaseUrl(final ImageType imageType) { | ||
return domain.getDomain() + imageType.getSaveDirectory(); | ||
} | ||
|
||
public void removeImage(final ImageInfo imageInfo, final ImageType imageType) { | ||
if(!imageClient.supportsDeletion()){ | ||
return; | ||
} | ||
//todo: 이미지 삭제? | ||
imageClient.delete(findResourcePath(imageInfo.getStoreName(), imageType)); | ||
} | ||
|
||
public String convertToImageUrl(final String fileName, final ImageType imageType) { | ||
return domain.convertToImageUrl(imageType.getSaveDirectory() + fileName); | ||
} | ||
|
||
public List<String> removeDomainFromUrl(final List<String> originalImageUrls, final ImageType imageType) { | ||
return domain.removeDomainFromUrl(originalImageUrls, imageType.getSaveDirectory()); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
backend/src/main/java/edonymyeon/backend/image/application/ImageType.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,18 @@ | ||
package edonymyeon.backend.image.application; | ||
|
||
public enum ImageType { | ||
|
||
POST("post/"), | ||
COMMENT("comment/"), | ||
PROFILE("profile/"); | ||
|
||
private final String saveDirectory; | ||
|
||
ImageType(final String saveDirectory) { | ||
this.saveDirectory = saveDirectory; | ||
} | ||
|
||
public String getSaveDirectory() { | ||
return saveDirectory; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 서비스는 migration 끝나면 제거할건가유?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
핫픽스 해야되지 않을까욤