API Gateway + LambdaアプリケーションでS3に署名付きURLを生成しリダイレクトする

API Gateway

こんにちは、菊池です。

やりたいこと

リクエストに応じてS3のファイルを加工し、ダウンロードするサーバレスアプリケーションを考えます。

presigned-api-002

単純なダウンロードであれば、S3から直接ダウンロードさせられますが、リクエストに応じたファイルの加工が必要なため、Lambdaでその処理を実行します。しかし、Lambdaのレスポンスサイズは最大6MBという制限があるため、そのままLambdaのレスポンスでダウンロードさせると、制限にかかるリスクがありました。

そこで、Lambdaで加工したファイルをテンポラリのS3バケットに一度保存し、署名付きURLを生成します。生成した署名付きURLにリダイレクトさせることで、ダウンロードはS3から直接行なってもらうようにします。

presigned-api-000

署名付きURLの生成と、API Gatewayのリダイレクトは以下の記事を参考にしています。

構築してみる

Lambda関数の作成

まずは、Lambda関数の作成です。Python 3.6のランタイムで以下のコードを実行します。ソースファイルのS3からファイルをコピーし、署名付きURLを生成しています。今回は試しなので特にファイルはいじらず、バケット間のコピーだけを行なっています。

import boto3

def lambda_handler(event, context):

    SOURCE_BUCKET = event['YOUR_BUCKET']
    KEY = event['YOUR_KEY']
    TARGET_BUCKET = 'tmp-bucket'

    s3 = boto3.client('s3')
    cp_object = s3.copy_object(
        CopySource = SOURCE_BUCKET + "/" + KEY,
        Bucket = TARGET_BUCKET,
        Key = KEY
        )
    header_location = s3.generate_presigned_url(
        ClientMethod = 'get_object',
        Params = {'Bucket' : TARGET_BUCKET, 'Key' : KEY},
        ExpiresIn = 3600,
        HttpMethod = 'GET'
        )

    result = {"Location": header_location}
    return result

元のファイルのS3バケット、ファイルパス(KEY)はリクエストのパラメータを利用します。

また、関数のロールにはS3のFullAccessポリシーを付与しておきましょう。

API Gatewayの作成

API GatewayでAPIを作成します。上記手順で作成したLambda関数を実行するPOSTメソッドを作成します。

作成したメソッドの、[メソッドレスポンス]と[統合レスポンス]を編集し、リダイレクトできるように設定します。

presigned-api-003

まずはメソッドレスポンスです。デフォルトにあるHTTPステータス、[200]は削除します。そして、[301]のステータスを作成し、レスポンスヘッダーにLocationを追加します。

presigned-api-004

続いて、統合レスポンスです。 こちらもデフォルトの[200]は削除し、メソッドレスポンスのステータス[301]を追加します。ヘッダーマッピングのLocationに、integration.response.body.Locationを追加し、保存しましょう。

presigned-api-005

実行してみる

作成したAPIを呼び出してみます。

$ curl -d '{ "YOUR_BUCKET": "SOURCEバケット", "YOUR_KEY": "test/testfile.png" }' -X POST https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/test/s3 -i
HTTP/1.1 301 Moved Permanently
Content-Type: application/json
Content-Length: 540
Connection: keep-alive
Date: Tue, 08 Aug 2017 08:00:40 GMT
x-amzn-RequestId: ab481103-7c0f-11e7-8f07-xxxxxxxxxxxx
Location: https://tmp-bucket.s3.amazonaws.com/test/testfile.png?AWSAccessKeyId=~~~~~省略~~~~~
X-Amzn-Trace-Id: sampled=0;root=1-59896fa7-xxxxxxxxxxxxxxxxxxxxxxxx
X-Cache: Miss from cloudfront
Via: 1.1 65986f5372ced7baa305aeec49e8e9c9.cloudfront.net (CloudFront)
X-Amz-Cf-Id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==

このように、301のレスポンスで作成された署名付きURLがLocationに指定されて帰ってきます。このURLにアクセス数すると、ちゃんと指定したファイルがダウンロードできました。

一時保存用のS3バケットは、ライフサイクルポリシーを指定し、一定時間後には削除されるようにしてしまってよいでしょう。

まとめ

以上です。

今回、Boto3での署名付きURLの生成、API Gateway + Lambdaのリダイレクトについてそれぞれ調べていたら、ともにDevelopers.ioの記事に当たりました。これらを組み合わせるだけで簡単にやりたいことを実装できました。

AWS Cloud Roadshow 2017 福岡