-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [chore] : S3 설정 파일 추가 * [feat] : S3 관련 에러 코드 정의 * [feat] : S3 파일(동영상, 이미지) 업로드 비즈니스 로직 작성 * [feat] : S3 컨트롤러 DTO 작성 * [feat] : s3 파일 업로드 컨트롤러 메서드 작성 * [fix] : s3 파일 확장자 집합에 . 포함 * [style] : 코드 리포멧팅 * [refactor] : 불필요한 출력문 삭제 * [feat] : 파일 오류 메시지 수정 * [feat] : jpaAuditing 설정 추가
- Loading branch information
Showing
10 changed files
with
238 additions
and
0 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
9 changes: 9 additions & 0 deletions
9
src/main/java/com/dnd/gongmuin/common/config/JpaAuditingConfig.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,9 @@ | ||
package com.dnd.gongmuin.common.config; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; | ||
|
||
@Configuration | ||
@EnableJpaAuditing | ||
public class JpaAuditingConfig { | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/com/dnd/gongmuin/common/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,33 @@ | ||
package com.dnd.gongmuin.common.config; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
import com.amazonaws.auth.AWSCredentials; | ||
import com.amazonaws.auth.AWSStaticCredentialsProvider; | ||
import com.amazonaws.auth.BasicAWSCredentials; | ||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.AmazonS3ClientBuilder; | ||
|
||
@Configuration | ||
public class S3Config { | ||
|
||
@Value("${cloud.aws.credentials.access-key}") | ||
private String accessKey; | ||
|
||
@Value("${cloud.aws.credentials.secret-key}") | ||
private String secretKey; | ||
|
||
@Value("${cloud.aws.region.static}") | ||
private String region; | ||
|
||
@Bean | ||
public AmazonS3 amazonS3Client() { | ||
AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); | ||
return AmazonS3ClientBuilder.standard() | ||
.withRegion(region) | ||
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) | ||
.build(); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/main/java/com/dnd/gongmuin/s3/controller/S3Controller.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,43 @@ | ||
package com.dnd.gongmuin.s3.controller; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.ModelAttribute; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import com.dnd.gongmuin.s3.dto.ImagesUploadRequest; | ||
import com.dnd.gongmuin.s3.dto.ImagesUploadResponse; | ||
import com.dnd.gongmuin.s3.dto.VideoUploadRequest; | ||
import com.dnd.gongmuin.s3.dto.VideoUploadResponse; | ||
import com.dnd.gongmuin.s3.service.S3Service; | ||
|
||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/files") | ||
public class S3Controller { | ||
|
||
private final S3Service s3Service; | ||
|
||
@PostMapping("/images") | ||
public ResponseEntity<ImagesUploadResponse> uploadImages( | ||
@ModelAttribute @Valid ImagesUploadRequest request | ||
) { | ||
List<String> imageUrls = s3Service.uploadImages(request.imageFiles()); | ||
return ResponseEntity.ok(ImagesUploadResponse.from(imageUrls)); | ||
} | ||
|
||
@PostMapping("/videos") | ||
public ResponseEntity<VideoUploadResponse> uploadVideo( | ||
@ModelAttribute @Valid VideoUploadRequest request | ||
) { | ||
String videoUrl = s3Service.uploadVideo(request.videoFile()); | ||
return ResponseEntity.ok(VideoUploadResponse.from(videoUrl)); | ||
} | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/dnd/gongmuin/s3/dto/ImagesUploadRequest.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,16 @@ | ||
package com.dnd.gongmuin.s3.dto; | ||
|
||
import java.util.List; | ||
|
||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
import jakarta.validation.constraints.Size; | ||
|
||
public record ImagesUploadRequest( | ||
|
||
@NotNull(message = "이미지 파일은 필수 입력 항목입니다.") | ||
@Size(min = 1, max = 10, message = "이미지는 1장 이상 10장 이하로 선택하세요.") | ||
List<MultipartFile> imageFiles | ||
) { | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/com/dnd/gongmuin/s3/dto/ImagesUploadResponse.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,11 @@ | ||
package com.dnd.gongmuin.s3.dto; | ||
|
||
import java.util.List; | ||
|
||
public record ImagesUploadResponse( | ||
List<String> imageUrls | ||
) { | ||
public static ImagesUploadResponse from(List<String> imageUrls) { | ||
return new ImagesUploadResponse(imageUrls); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/com/dnd/gongmuin/s3/dto/VideoUploadRequest.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,11 @@ | ||
package com.dnd.gongmuin.s3.dto; | ||
|
||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record VideoUploadRequest( | ||
@NotNull(message = "비디오 파일은 필수 입력 항목입니다.") | ||
MultipartFile videoFile | ||
) { | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/dnd/gongmuin/s3/dto/VideoUploadResponse.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,9 @@ | ||
package com.dnd.gongmuin.s3.dto; | ||
|
||
public record VideoUploadResponse( | ||
String videoUrl | ||
) { | ||
public static VideoUploadResponse from(String videoUrl) { | ||
return new VideoUploadResponse(videoUrl); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/main/java/com/dnd/gongmuin/s3/exception/S3ErrorCode.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,19 @@ | ||
package com.dnd.gongmuin.s3.exception; | ||
|
||
import com.dnd.gongmuin.common.exception.ErrorCode; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public enum S3ErrorCode implements ErrorCode { | ||
EMPTY_FILE_NAME("원본 파일명은 필수입니다.", "S3_001"), | ||
INVALID_FILE_EXTENSION("잘못된 되었거나 지원하지 않는 파일 형식입니다.", "S3_002"), | ||
|
||
FAILED_TO_UPLOAD("파일을 업로드하는데 실패했습니다.", "S3_003"); | ||
|
||
|
||
private final String message; | ||
private final String code; | ||
} |
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,81 @@ | ||
package com.dnd.gongmuin.s3.service; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import java.util.UUID; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.model.ObjectMetadata; | ||
import com.amazonaws.services.s3.model.PutObjectRequest; | ||
import com.dnd.gongmuin.common.exception.runtime.ValidationException; | ||
import com.dnd.gongmuin.s3.exception.S3ErrorCode; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class S3Service { | ||
|
||
private static final String IMAGE_FOLDER_NAME = "images"; | ||
private static final String VIDEO_FOLDER_NAME = "videos"; | ||
private static final Set<String> ALLOWED_FILE_EXTENSIONS = Set.of(".jpg", ".jpeg", ".png", ".mp4", ".avi", ".mov"); | ||
private final AmazonS3 amazonS3; | ||
|
||
@Value("${cloud.aws.s3.bucket}") | ||
private String bucket; | ||
|
||
public List<String> uploadImages(List<MultipartFile> multipartFileList) { | ||
List<String> fileUrls = new ArrayList<>(); | ||
|
||
for (MultipartFile multipartFile : multipartFileList) { | ||
fileUrls.add(uploadFile(multipartFile, IMAGE_FOLDER_NAME)); | ||
} | ||
return fileUrls; | ||
} | ||
|
||
public String uploadVideo(MultipartFile multipartFile) { | ||
return uploadFile(multipartFile, VIDEO_FOLDER_NAME); | ||
} | ||
|
||
private String uploadFile(MultipartFile multipartFile, String folderName) { | ||
ObjectMetadata metadata = new ObjectMetadata(); | ||
String fileName = createFileName(multipartFile.getOriginalFilename()); | ||
metadata.setContentLength(multipartFile.getSize()); | ||
metadata.setContentType(multipartFile.getContentType()); | ||
|
||
String bucketName = bucket + "/" + folderName; | ||
|
||
try { | ||
amazonS3.putObject( | ||
new PutObjectRequest(bucketName, fileName, multipartFile.getInputStream(), metadata) | ||
); | ||
|
||
} catch (IOException exception) { | ||
throw new ValidationException(S3ErrorCode.FAILED_TO_UPLOAD); | ||
} | ||
return amazonS3.getUrl(bucketName, fileName).toString(); | ||
} | ||
|
||
private String createFileName(String fileName) { | ||
return UUID.randomUUID().toString().concat(getFileExtension(fileName)); | ||
} | ||
|
||
private String getFileExtension(String fileName) { | ||
if (Objects.isNull(fileName) || fileName.isBlank()) { | ||
throw new ValidationException(S3ErrorCode.EMPTY_FILE_NAME); | ||
} | ||
String extension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(); | ||
if (!ALLOWED_FILE_EXTENSIONS.contains(extension)) { | ||
throw new ValidationException(S3ErrorCode.INVALID_FILE_EXTENSION); | ||
} | ||
return extension; | ||
} | ||
|
||
} |