S3に画像アップロードをトリガーにLambda関数を実行してサムネイルを作成する

目次

S3バケットへの画像アップロードをトリガーにして、Lambda関数を実行してサムネイルを作成する

S3が提供する機能の1つに、イベント通知機能があります。

Amazon S3 イベント通知機能を使用して、S3 バケットで特定のイベントが発生したときに通知を受け取ることができます。

Amazon S3 イベント通知

今回はS3イベント通知をトリガーにして、Lambda関数を実行することを考えます。
具体的には、以下のAWS公式のチュートリアルで取り上げられている構成を再現します。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-tutorial.html

S3バケットに画像がアップロードされると、そのイベント通知をトリガーとして、サムネイルを作成するLambda関数が実行されます。

構築する環境

Diagram of trigger image upload to S3 invokes Lambda function to create thumbnails.

2つのS3バケットを作成します。

一方のバケットは、ユーザからアップロードされた画像を保存するバケットです。
このバケットは通知機能を有効化します。

もう一方のバケットは、関数が作成したサムネイルを保存するバケットです。

Lambda関数を作成します。
この関数の働きは、画像を読み込んでサムネイルを作成することです。
この関数は1つ目のバケットの通知がトリガーとなって実行されます。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-dva/tree/main/03/005

テンプレートファイルのポイント解説

S3バケット

バケット1

Resources:
  UploadBucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      BucketName: !Ref UploadBucketName
      NotificationConfiguration:
        LambdaConfigurations:
          - Event: "s3:ObjectCreated:*"
            Function: !Ref ThumbnailFunctionArn
Code language: YAML (yaml)

サムネイルの元となる画像を配置するバケットです。
NotificationConfigurationプロパティで通知設定を行います。
今回の構成では、通知先にLambda関数を指定しますので、LambdaConfigurationsプロパティを使用します。
画像がバケットに設置された際に、Lambda関数にイベント通知するため、Eventプロパティに「s3:ObjectCreated:*」を、Functionプロパティに後述の関数を指定します。

バケット2

Resources:
  ResizeBucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      BucketName: !Ref ResizeBucketName
Code language: YAML (yaml)

関数が生成したサムネイル画像を配置するバケットです。

特別な設定は行いません。

Lambda

関数

Resources:
  ThumbnailFunction:
    Type: AWS::Lambda::Function
    Properties:
      Architectures:
        - !Ref Architecture
      Code:
        ZipFile: |
          import boto3
          import os
          import sys
          import uuid
          from urllib.parse import unquote_plus
          from PIL import Image
          import PIL.Image

          s3_client = boto3.client('s3')

          def resize_image(image_path, resized_path):
            with Image.open(image_path) as image:
                image.thumbnail(tuple(x / 2 for x in image.size))
                image.save(resized_path)

          def lambda_handler(event, context):
            for record in event['Records']:
                bucket = record['s3']['bucket']['name']
                key = unquote_plus(record['s3']['object']['key'])
                tmpkey = key.replace('/', '')
                download_path = '/tmp/{}{}'.format(uuid.uuid4(), tmpkey)
                upload_path = '/tmp/resized-{}'.format(tmpkey)
                s3_client.download_file(bucket, key, download_path)
                resize_image(download_path, upload_path)
                s3_client.upload_file(upload_path, '{}-resized'.format(bucket), key)
      FunctionName: !Sub "${Prefix}-ThumbnailFunction"
      Handler: !Ref Handler
      Layers:
        - !Ref LambdaLayer
      Runtime: !Ref Runtime
      Role: !GetAtt ThumbnailFunctionRole.Arn
      Timeout: !Ref Timeout
Code language: YAML (yaml)

Lambda関数で実行するコードをインライン形式で記載します。
詳細につきましては、以下のページをご確認ください。

あわせて読みたい
CloudFormationでLambdaを作成する3パータン(S3/インライン/コンテナ) 【CloudFormationでLambdaを作成する】 CloudFormationでLambdaを作成する場合、大別すると以下の3パターンあります。 S3バケットにコードをアップロードする インライ...

実行するコードですが、以下のAWS公式ページに記載されているコードを使用します。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-tutorial.html

コードの内容ですが、以下の流れです。

  • 受け取ったイベント通知から、通知元のバケット名や配置されたオブジェクト名を取得する。
  • S3バケットからオブジェクトをローカルストレージにダウンロードする。
  • ダウンロードしたオブジェクトをリサイズしてサムネイルを作成する。
  • サムネイルをバケットに配置する。

この関数用のIAMロールを確認します。

Resources:
  ThumbnailFunctionRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: CreateThumbnailPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                Resource:
                  - !Sub "arn:aws:s3:::${UploadBucketName}/*"
              - Effect: Allow
                Action:
                  - s3:PutObject
                Resource:
                  - !Sub "arn:aws:s3:::${ResizeBucketName}/*"
Code language: YAML (yaml)

2つのバケットに対してのアクションを許可する内容です。
画像をアップロードするバケットに対しては、関数は画像をダウンロードしますので、「s3:GetObject」の権限を与えます。
サムネイルを配置するバケットに対しては、関数は画像をアップロードしますので、「s3:PutObject」の権限を与えます。

Lambdaレイヤー

Lambda関数がサムネイルを作成する過程で、Pillowパッケージを使用します。
外部パッケージは、Lambdaレイヤーを作成し、こちらにパッケージを含めることで、Lambda関数がクライアントモジュールをインポートすることができるようになります。
今回はCloudFormationカスタムリソースを使用することによって、このLambdaレイヤーの作成を自動的に実行します。
詳細については、以下のページをご確認ください。

あわせて読みたい
CFNカスタムリソースでLambdaレイヤーパッケージを準備する – Python版 【CloudFormationカスタムリソースを使って、Python用のLambdaレイヤーパッケージを自動的に作成・配置する】 以下のページでLambdaレイヤーの作成方法について取り上げ...

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • 画像アップロード用S3バケット:dva-03-005
  • サムネイル設置用S3バケット:dva-03-005-resized
  • サムネイル作成用Lambda関数:dva-03-005-ThumbnailFunction

AWS Management Consoleから各リソースを確認します。

まず画像アップロード用バケットを確認します。

Detail of S3 1.
Detail of S3 2.

イベント通知に関する設定があります。
オブジェクト作成に関する全てのイベントを対象として、後述のLambda関数を実行する内容です。

Lambda関数を確認します。

Detail of Lambda 1

関数が正常に作成されています。
AWS公式ページで紹介されているコードを実行します。

動作確認

準備が整いましたので、バケットに画像を配置します。
AWS Management Consoleから画像をバケットに配置します。

Detail of S3 3.

テスト用の画像として、今回の構成図をアップロードします。

Detail of S3 4.

アップロードが完了しました。

オブジェクトをアップロードすることによって、Lambda関数にイベント通知されたはずです。
関数の実行ログを確認します。

Detail of Lambda 2

関数が自動的に実行されています。

サムネイル用バケットを確認します。

Detail of S3 5.

画像が設置されています。
イベント通知によって、自動的にLambda関数が実行されて、サムネイル画像を作成し、このバケットに設置しています。

最後に作成されたサムネイルを確認します。

Resized Image.

確かにリサイズされています。

このようにS3イベント通知を使用することによって、任意のイベント発生時に、Lambda関数を実行することができます。

まとめ

S3バケットに画像がアップロードされると、そのイベント通知をトリガーとして、サムネイルを作成するLambda関数が実行される構成を確認しました。

目次