Skip to content
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

Merged
merged 3 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ dependencies {

// SES
implementation 'com.amazonaws:aws-java-sdk-ses:1.12.778'

//S3
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.777'
}

spotless {
Expand Down
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
Expand Up @@ -5,6 +5,8 @@
import com.amazonaws.regions.Regions;
import com.amazonaws.services.kinesisvideo.AmazonKinesisVideo;
import com.amazonaws.services.kinesisvideo.AmazonKinesisVideoClient;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -25,6 +27,32 @@ public class AwsConfig {
@Value("${aws.ses.secret-key}")
private String sesSecretKey;

@Value("${aws.s3.access-key}")
private String s3AccessKey;

@Value("${aws.s3.secret-key}")
private String s3SecretKey;

@Bean
public AmazonS3 amazonS3() {
AWSCredentials awsCredentials =
new AWSCredentials() {
@Override
public String getAWSAccessKeyId() {
return s3AccessKey;
}

@Override
public String getAWSSecretKey() {
return s3SecretKey;
}
};
return AmazonS3Client.builder()
.withRegion(Regions.AP_NORTHEAST_2)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();
}

@Bean
public AmazonKinesisVideo amazonKinesisVideo() {
AWSCredentials awsCredentials =
Expand Down
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 = "담당자: 최민석")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네.. ㅋㅋㅋㅋ ㅜㅜ

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

잘하셨어요..

Copy link
Member Author

Choose a reason for hiding this comment

The 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
@@ -1,4 +1,4 @@
package org.ioteatime.meonghanyangserver.video.entity;
package org.ioteatime.meonghanyangserver.video.domain;

import jakarta.persistence.*;
import java.time.LocalDateTime;
Expand Down
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);
}
}
7 changes: 6 additions & 1 deletion src/main/resources/application-key.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 환경변수 객체로 분리해서 같이 수정해 주시면 좋을 거 같아요 !!


Loading