S3 Presigned URL을 이용해 클라이언트에서 직접 컨텐츠 업로드 구현방법(feat. ruby && aws-sdk-s3)

Posted by negabaro kim on Saturday, May 1, 2021 Tags: rails   3 minute read

통상적으로 컨텐츠의 업로드는 인증로직과 관계되어 서버에서 처리되는 경우가 많다.

한편 동영상을 업로드하는 경우, 여러 확장자 파일과 수GB파일 단위의 영상파일을 업로드하게 되는데 이때 서버를 경유해서 처리시 아래와 같은 문제가 있다.

  1. 비효율적인 리소스 활용 (영상을 서버에 넘겨주는 처리와 서버에서 영상을 스토리지에 업로드하는 작업)
  2. 서버의 스토리지 용량관리
  3. 각종 대응(타임아웃, 동기화/비동기화 고려)

이러한 문제를 해결하기 위해 컨텐츠(영상)을 서버에 보내서 업로드하지 않고 클라이언트에서 직접 스토리지(이 포스트에서 소개하는 스토리지는 S3)에 업로드 하는 방법이 등장했다.

S3에서는 pre-signed URL을 이용해 구현이 가능하다.

image

S3의 Presigned URL에 대해서는 다른 포스트에서 자세히 설명할 예정이다. 이 포스트에서는 가볍게 사용방법만 언급한다.

대략적인 구현방법

1.

서버에서 어떤 디렉토리에 컨텐츠를 업로드 할것이라는 정보를 관리한다. 해당 정보를 A라는 변수에 담았다고 해보자.

A = "#{dir}/#{file_name}"

2.

A라는 변수에 있는 path를 이용해presigned_url(:put)을 발행해준다.

ruby코드로는 아래와 같다.

bucket = Aws::S3::Resource.new.bucket(ENV['BUCKET_NAME'])
obj = bucket.object("#{dir}/#{file_name}")
obj.presigned_url(:put)

3.

presigned_url(:put)을 실행해주면 크게 두가지 액션이 실행된다.

실제 해당 S3상에 0byte의 파일이 생성됨

bucket명 xxx에 images/xx.mp4 라는 path였다면 해당 path의 S3에 0byte의 파일이 만들어진다.

presigned_url 발행

아래와 같은 presigned url이 발행된다. 해당 URL은 디폴트 유효기간이 15분이고(최대 7일까지 가능) 그 시간이 지나면 사용이 불가능하다.

https://xxx.s3.amazonaws.com/images/xx.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAVZH4SBSY4EO3QIWS%2F20210428%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210428T112140Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=20e494910e73bde0404aa4ccaf6b10a8690a3ceff16489e2e72a82b879a4d39f

4.

서버는 3에서 생성한 presigned_url을 클라이언트에 넘겨준다.

5.

클라이언트는 4에서 전달받은 presigned_url을 이용해 직접 파일업로드가 가능하다.

해당 url에 REST로 put을 해주면 업로드가 가능하다.

flutter에서 image파일 업로드시 사용하는 코드를 첨부해둔다.

  Future<void> uploadImage(String url, File image) {

    final Dio dio = Dio()..interceptors.add(LogInterceptor(responseBody: true));
    dio.options = BaseOptions(contentType: 'image/jpeg');
    return dio.putUri<void>(Uri.parse(url),
        data: image.openRead(),
        options: Options(headers: <String, dynamic>{
          HttpHeaders.contentTypeHeader: ContentType('image', 'jpeg'),
          HttpHeaders.contentLengthHeader: image.lengthSync(),
        }));
  }

클라이언트에서 작업할때 알고 있으면 좋을 포인트로는 아래와 같은것이 있다.

  • presigned_url을 이용하면 업로드할 s3 bucket policy를 무시한다.(s3:PutObject Deny라고 해도 가능?)
  • presigned_url을 디폴트 유효기간인 15분내에 업로드가 가능(시간이 지나면 불가능하므로 15분내에 처리해야함)
  • 특정 object의 acl권한도 안보는듯?(FULL_CONTROL망 있고 public-write 아니어도 가능했음, FULL_CONTROL이 필수인지는 미검증)

메모

예전 기사를 보면 S3 put처리는 용량제한이 있는듯??

컨텐츠의 용량에 따라 lambda등을 경유해서 처리할 필요가 있을지도