Little bIT awesome

[ACC project 2주차 회고록] S3, Presigned_url 본문

인프라/AWS

[ACC project 2주차 회고록] S3, Presigned_url

까루카라 2024. 7. 28. 23:20

Amazon S3

Amazon S3는 AWS(Amazon Web Services)에서 제공하는 객체 스토리지 서비스로, 데이터를 인터넷 규모로 저장하고 검색할 수 있도록 설계되었다.

 

주요 기능과 특징

  • 안정성: S3는 높은 내구성과 가용성을 제공
  • 확장성: 데이터 양에 관계없이 자동으로 확장
  • 보안: 다양한 보안 기능(암호화, 접근 제어 등)을 제공
  • 유연한 데이터 모델: 파일을 객체로 저장하고, 각 객체는 고유한 키를 가진다.
  • 비용 효율성: 사용한 만큼만 비용을 지불하는 구조

Pre-signed URL

Pre-signed URL은 특정한 S3 객체에 대한 제한된 시간 동안의 접근 권한을 부여하는 URL입니다. 이를 통해 S3 버킷에 저장된 파일을 안전하게 공유할 수 있습니다. Pre-signed URL을 사용하면, 파일을 다운로드하거나 업로드할 수 있는 링크를 생성할 수 있습니다.

특징

  • 유효 기간: Pre-signed URL은 생성 시 설정된 유효 기간 동안만 유효합니다.
  • 제한된 권한: URL을 통해 특정 작업(예: 다운로드, 업로드)만 허용됩니다.
  • 보안: URL이 만료되면 더 이상 접근할 수 없으므로, 데이터 유출 위험을 줄일 수 있습니다.

 

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class AWSS3Config {
    @Value("${amazon.aws.accessKey}")
    private String accessKey;
    @Value("${amazon.aws.secretKey}")
    private String secretKey;
    @Value("${amazon.aws.region}")
    private String region;

    @Bean
    @Primary
    public BasicAWSCredentials awsCredentialsProvider(){
        BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey, secretKey);
        return basicAWSCredentials;
    }

    @Bean
    public AmazonS3 amazonS3() {
        AmazonS3 s3Builder = AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(awsCredentialsProvider()))
                .build();
        return s3Builder;
    }

}

 

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.Headers;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.DeleteObjectRequest;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.solux.greenish.Photo.Domain.Photo;
import com.solux.greenish.Photo.Dto.PhotoResponseDto;
import com.solux.greenish.Photo.Dto.PresignedUrlDto;
import com.solux.greenish.Photo.Repository.PhotoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.net.URL;
import java.util.Date;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class PhotoService {

    private final PhotoRepository photoRepository;
    @Value("${amazon.aws.bucket}")
    private String bucket;

    private final AmazonS3 amazonS3;

    // 업로드 할 때 preSignedUrl 생성
    @Transactional
    public PhotoResponseDto getPreSignedUrl(PresignedUrlDto request) {
        //String prefix, String fileName
        String photoPath = createPath(String.valueOf(request.getPrefix()), request.getFileName());

        Photo photo = Photo.builder()
                .photoPath(photoPath)
                .fileName(request.getFileName())
                .build();
        photoRepository.save(photo);

        GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(bucket, photoPath, HttpMethod.GET);
        URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest);
        return PhotoResponseDto.builder()
                .photoId(photo.getId())
                .url(url.toString())
                .build();
    }

    public PhotoResponseDto generatePreSignedDto(Long prefix, String fileName) {
        return getPreSignedUrl(PresignedUrlDto.builder()
                .prefix(prefix)
                .fileName(fileName)
                .build());

    }

    // 포토 삭제
    @Transactional
    public void deletePhoto(Long photoId) {
        Photo photo = photoRepository.findById(photoId)
                .orElseThrow(() -> new IllegalArgumentException("해당 사진을 찾을 수 없습니다. "));
        amazonS3.deleteObject(new DeleteObjectRequest(bucket, photo.getPhotoPath()));

        photoRepository.delete(photo);
    }


    // photo_id에서 조회
    @Transactional(readOnly = true)
    public PhotoResponseDto getFilePath(Long photo_id) {
        Photo photo = photoRepository.findById(photo_id)
                .orElseThrow(() -> new IllegalArgumentException("해당 사진 파일이 존재하지 않습니다. "));
        String photoPath = photo.getPhotoPath();
        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket, photoPath)
                .withMethod(HttpMethod.GET);

        URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest);
        return PhotoResponseDto.builder()
                .photoId(photo.getId())
                .url(url.toString())
                .build();
    }

    /**
     * 파일 업로드용(PUT) presigned url 생성
     * @param bucket 버킷 이름
     * @param fileName S3 업로드용 파일 이름
     * @return presigned url
     */
    private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String bucket, String fileName, HttpMethod httpMethod) {
        GeneratePresignedUrlRequest generatePresignedUrlRequest =
                new GeneratePresignedUrlRequest(bucket, fileName)
                        .withMethod(httpMethod)
                        .withExpiration(getPreSignedUrlExpiration());
        generatePresignedUrlRequest.addRequestParameter(
                Headers.S3_CANNED_ACL,
                CannedAccessControlList.PublicRead.toString());
        return generatePresignedUrlRequest;
    }

    /**
     * presigned url 유효 기간 설정
     * @return 유효기간
     */
    private Date getPreSignedUrlExpiration() {
        Date expiration = new Date();
        long expTimeMillis = expiration.getTime();
        expTimeMillis += 1000 * 60 * 2;
        expiration.setTime(expTimeMillis);
        return expiration;
    }

    /**
     * 파일 고유 ID를 생성
     * @return 36자리의 UUID
     */
    private String createFileId() {
        return UUID.randomUUID().toString();
    }

    /**
     * 파일의 전체 경로를 생성
     * @param prefix 디렉토리 경로
     * @return 파일의 전체 경로
     */
    private String createPath(String prefix, String fileName) {
        String fileId = createFileId();
        return String.format("%s/%s", prefix, fileId + fileName);
    }
}

'인프라 > AWS' 카테고리의 다른 글

[ACC 프로젝트 1주차] 회고록  (0) 2024.07.21
[ACC] Network - CloudFront, ELB  (0) 2024.05.09
[ACC] Computing - EC2, Lightsail  (0) 2024.05.05