-
Notifications
You must be signed in to change notification settings - Fork 1
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: S3 생성 및 동영상 presigned url 조회 구현 #150
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package org.ioteatime.meonghanyangserver.clients.s3; | ||
|
||
import com.amazonaws.HttpMethod; | ||
import com.amazonaws.services.s3.AmazonS3; | ||
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; | ||
import java.net.URL; | ||
import java.util.Calendar; | ||
import java.util.Date; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class S3Client { | ||
private final AmazonS3 amazonS3; | ||
|
||
@Value("${aws.s3.bucket}") | ||
private String bucket; | ||
|
||
public String generatePreSignUrl(String filename, HttpMethod httpMethod) { | ||
Calendar calendar = Calendar.getInstance(); | ||
calendar.setTime(new Date()); | ||
calendar.add(Calendar.MINUTE, 10); | ||
GeneratePresignedUrlRequest generatePresignedUrlRequest = | ||
new GeneratePresignedUrlRequest(bucket, filename) | ||
.withMethod(httpMethod) | ||
.withExpiration(calendar.getTime()); | ||
URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest); | ||
return url.toString(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.ioteatime.meonghanyangserver.common.type; | ||
|
||
public enum VideoErrorType implements ErrorTypeCode { | ||
VIDEO_NOT_FOUND("VIDEO NOT FOUND", "동영상 정보를 찾을 수 없습니다."); | ||
|
||
private final String message; | ||
private final String description; | ||
|
||
VideoErrorType(String message, String description) { | ||
this.message = message; | ||
this.description = description; | ||
} | ||
|
||
@Override | ||
public String getMessage() { | ||
return this.message; | ||
} | ||
|
||
@Override | ||
public String getDescription() { | ||
return this.description; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.ioteatime.meonghanyangserver.common.type; | ||
|
||
public enum VideoSuccessType implements SuccessTypeCode { | ||
GET_PRESIGNED_URL(200, "OK", "Presigned Url 조회에 성공하였습니다."); | ||
|
||
private final Integer code; | ||
private final String message; | ||
private final String description; | ||
|
||
VideoSuccessType(Integer code, String message, String description) { | ||
this.code = code; | ||
this.message = message; | ||
this.description = description; | ||
} | ||
|
||
@Override | ||
public Integer getCode() { | ||
return this.code; | ||
} | ||
|
||
@Override | ||
public String getMessage() { | ||
return this.message; | ||
} | ||
|
||
@Override | ||
public String getDescription() { | ||
return this.description; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package org.ioteatime.meonghanyangserver.video.controller; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import org.ioteatime.meonghanyangserver.common.api.Api; | ||
import org.ioteatime.meonghanyangserver.common.utils.LoginMember; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
|
||
@Tag(name = "Video Api", description = "Video 관련 API 목록입니다.") | ||
public interface VideoApi { | ||
@Operation(summary = "동영상의 presigned url을 발급받습니다..", description = "담당자: 최민석") | ||
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. 잘하셨어요.. 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. 감사합니다.. |
||
public Api<?> getVideoPresignedUrl( | ||
@LoginMember Long memberId, @PathVariable Long videoId, @PathVariable Long groupId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.ioteatime.meonghanyangserver.video.controller; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.ioteatime.meonghanyangserver.common.api.Api; | ||
import org.ioteatime.meonghanyangserver.common.type.VideoSuccessType; | ||
import org.ioteatime.meonghanyangserver.common.utils.LoginMember; | ||
import org.ioteatime.meonghanyangserver.video.dto.response.VideoPresignedUrlResponse; | ||
import org.ioteatime.meonghanyangserver.video.service.VideoService; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/video") | ||
public class VideoController implements VideoApi { | ||
private final VideoService videoService; | ||
|
||
@GetMapping("/{videoId}/group/{groupId}") | ||
public Api<VideoPresignedUrlResponse> getVideoPresignedUrl( | ||
@LoginMember Long memberId, @PathVariable Long videoId, @PathVariable Long groupId) { | ||
VideoPresignedUrlResponse videoPresignedUrlResponse = | ||
videoService.getVideoPresignedUrl(memberId, videoId, groupId); | ||
return Api.success(VideoSuccessType.GET_PRESIGNED_URL, videoPresignedUrlResponse); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package org.ioteatime.meonghanyangserver.video.dto.response; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.NotBlank; | ||
|
||
@Schema(description = "동영상 presigned url 조회 응답") | ||
public record VideoPresignedUrlResponse( | ||
@NotBlank | ||
@Schema( | ||
description = "동영상 presigned url", | ||
example = "https://bucket.s3.ap-northeast-2.amazonaws.com/test?") | ||
String presignedUrl) {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package org.ioteatime.meonghanyangserver.video.mapper; | ||
|
||
import org.ioteatime.meonghanyangserver.video.dto.response.VideoPresignedUrlResponse; | ||
|
||
public class VideoResponseMapper { | ||
public static VideoPresignedUrlResponse form(String presignedURL) { | ||
return new VideoPresignedUrlResponse(presignedURL); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package org.ioteatime.meonghanyangserver.video.repository; | ||
|
||
import org.ioteatime.meonghanyangserver.video.domain.VideoEntity; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface JpaVideoRepository extends JpaRepository<VideoEntity, Long> {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package org.ioteatime.meonghanyangserver.video.repository; | ||
|
||
import java.util.Optional; | ||
import org.ioteatime.meonghanyangserver.video.domain.VideoEntity; | ||
|
||
public interface VideoRepository { | ||
Optional<VideoEntity> findById(Long videoId); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.ioteatime.meonghanyangserver.video.repository; | ||
|
||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import java.util.Optional; | ||
import lombok.RequiredArgsConstructor; | ||
import org.ioteatime.meonghanyangserver.video.domain.VideoEntity; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
public class VideoRepositoryImpl implements VideoRepository { | ||
private final JpaVideoRepository jpaVideoRepository; | ||
private final JPAQueryFactory jpaQueryFactory; | ||
|
||
@Override | ||
public Optional<VideoEntity> findById(Long videoId) { | ||
return jpaVideoRepository.findById(videoId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.ioteatime.meonghanyangserver.video.service; | ||
|
||
import com.amazonaws.HttpMethod; | ||
import lombok.RequiredArgsConstructor; | ||
import org.ioteatime.meonghanyangserver.clients.s3.S3Client; | ||
import org.ioteatime.meonghanyangserver.common.exception.NotFoundException; | ||
import org.ioteatime.meonghanyangserver.common.type.GroupErrorType; | ||
import org.ioteatime.meonghanyangserver.common.type.VideoErrorType; | ||
import org.ioteatime.meonghanyangserver.groupmember.repository.GroupMemberRepository; | ||
import org.ioteatime.meonghanyangserver.video.domain.VideoEntity; | ||
import org.ioteatime.meonghanyangserver.video.dto.response.VideoPresignedUrlResponse; | ||
import org.ioteatime.meonghanyangserver.video.mapper.VideoResponseMapper; | ||
import org.ioteatime.meonghanyangserver.video.repository.VideoRepository; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class VideoService { | ||
private final VideoRepository videoRepository; | ||
private final GroupMemberRepository groupMemberRepository; | ||
private final S3Client s3Client; | ||
|
||
public VideoPresignedUrlResponse getVideoPresignedUrl( | ||
Long memberId, Long videoId, Long groupId) { | ||
if (!groupMemberRepository.existsByMemberIdAndGroupId(memberId, groupId)) { | ||
throw new NotFoundException(GroupErrorType.GROUP_MEMBER_NOT_FOUND); | ||
} | ||
VideoEntity videoEntity = | ||
videoRepository | ||
.findById(videoId) | ||
.orElseThrow(() -> new NotFoundException(VideoErrorType.VIDEO_NOT_FOUND)); | ||
String presignedURL = | ||
s3Client.generatePreSignUrl(videoEntity.getVideoName(), HttpMethod.GET); | ||
return VideoResponseMapper.form(presignedURL); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,9 @@ aws: | |
secret-key: ${AWS_KVS_SECRET_KEY} | ||
ses: | ||
access-key: ${AWS_SES_ACCESS_KEY} | ||
secret-key: ${AWS_SES_SECRET_KEY} | ||
secret-key: ${AWS_SES_SECRET_KEY} | ||
s3: | ||
access-key: ${AWS_S3_ACCESS_KEY} | ||
secret-key: ${AWS_S3_SECRET_KEY} | ||
bucket: ${AWS_S3_BUCKET} | ||
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. 요거 환경변수 객체로 분리해서 같이 수정해 주시면 좋을 거 같아요 !! |
||
|
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.
네.. ㅋㅋㅋㅋ ㅜㅜ