★★★ Twitterやってます。フォローして頂けると嬉しいです(*^^*) ★★★

S3にアップロードした画像のサムネイルを自動で生成する(前編)

IT
スポンサーリンク

今回やること

AWSのストレージサービス(S3)アップロードした写真や画像ファイルから自動でサムネイル画像を作成してみようと思います。

このぐらいの簡単な処理ならEC2などでサーバー立ててプログラム実行環境を構築するより、今どきのサーバレスでプログラムだけ書いてささっと処理してしまった方がコストが安いですし、スマートですので、 AWS Lambdaを使ってやってみます。

準備

必要なもの
  • AWSアカウント
  • 少しのAWS知識
  • 少しのPythonの知識 or 他のプログラム言語の知識

AWSのアカウントは無料でできますので、まだ作成していない方はこちらの記事でアカウントの作り方を紹介していますので、ご覧になってください。

今回使うAWSのサービス
  • S3: ストレージ
  • Lambda: サーバレス関数
  • IAM: 権限管理
スポンサーリンク

S3に写真・画像をアップロードしたら自動でサムネ画像作成

大まかな流れは、まずはファイルをアップロード先のS3バケットを作ります、

次にバケットへの写真、画像ファイルのアップロードを検知したら、その写真、画像ファイルのサムネイルを生成する処理をLambda関数として作成します。

写真、画像ファイルのアップロード先のS3バケットを作る

サービス検索欄に「S3」と入力し、S3が表示されたらクリックします。

まずは、バケットを作成します。バケットとはファイルを格納するための入れ物だと思ってください。

今回は写真や画像ファイルをアップロードする先のバケットを作成します。今回のバケット名は「image-files-box」にしました。

AWS Lambda関数を作成

Lambda実行用のロールは前回作成済みなので今回はそれを使います。

一から作成を選択して新しい関数を作成します。

Lambda関数の新規作成

サービス検索欄に「lambda」と入力し、Lambdaをクリックします。

①関数に名前を付けます。

②今回は画像処理ライブラリPillowを使いたいので、Pythonを選択します。

③と④はLambdaの実行ロールを選択します。前回作ったS3処理用の共通ロールを使います。

トリガーの登録

Lambda関数は作っただけではなにも起こりません。なにかをきっかけにその関数を呼び出してもらう必要があり、それを設定するのがこのトリガー登録です。

今回はS3バケットにファイルがアップロードされたタイミングで動く関数を作るので、次のようにセットします。

①トリガーはS3を選択

②監視対象のバケット名を選択します。今回は「image-files-box」バケットを選択します。

③対象のイベントはすべてのオブジェクト作成イベントを選択します。

④「original」フォルダ内にファイルが格納された時だけ発動するようにします。

⑤画像処理するので、拡張子指定でJPGにしてみます。

今回は自動生成したサムネイル画像も同じバケットに格納しようと思っているので、格納先を間違えると無限ループに陥る危険がありますのでお試しの際は自己責任でお願いします。心配な方は出力先バケットを分けると安全です。

サムネイル画像作成コードの作成

次はサムネ画像を作成する処理を書きます。PythonではPillowという画像処理ライブラリがありますので、それを使います。

バケットに写真ファイルをアップロードすると、さきほど登録したトリガーによってこのLambda関数がコールされます。

デフォルトでは、lambda_function.py の lambda_handler関数が呼び出されますので、そこに処理を書きます。

S3のバケットにアップロードされたファイルは関数コール時の引数 event の中に入ってますので、そこから取り出します。取り出す時は、S3へのアクセスライブラリを使ってLambda実行環境の /tmp にダウンロードし、画像ファイルのサイズを半分にしてthumbnails フォルダ内にアップロードします。

import boto3
import os
import sys
import uuid
from urllib.parse import unquote_plus
from PIL import Image
import PIL.Image
s3cli = boto3.client('s3')

def resize_image(image_path, resized_path):
    with Image.open(image_path) as image:
        image.thumbnail(tuple(n / 2 for n 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'])
        splitkey = key.split('/')
        newkey = key.replace(splitkey[0], 'thumbnails')
        tmpkey = key.replace('/', '')	
        download_path = '/tmp/{}{}'.format(uuid.uuid4(), tmpkey)
        upload_path = '/tmp/resized-{}'.format(tmpkey)
        s3cli.download_file(bucket, key, download_path)
        resize_image(download_path, upload_path)
        s3cli.upload_file(upload_path, bucket, newkey)
スポンサーリンク

動作確認

さて、ここまでできたら、実際に動かしてみましょう。

バケット内にフォルダを作成

さきほどトリガーをセットしたバケットに新しいフォルダを作ります。

トリガー設定時のプレフィックスに「original/」を指定したので、originalフォルダを作成してその中に写真、画像ファイルをアップロードしないとトリガーが発動しません。

作成するとこのように作成したフォルダが表示されます。

この「original」フォルダ内に写真、画像ファイルをアップロードするとトリガーが発動して、Lambda関数がコールされて実行されます。

処理が動くとさきほどのコードに書いた出力先フォルダ「thumbnails」がバケット内に作成されて、生成したサムネイル画像ファイルが作成されます。

さて、サムネイル画像はできたかな~?

・・・できていない(T_T)

コード実行の結果を確認

実行結果を確認するには、「モニタリング」タブをクリックし、「CloudWatchのログを表示」をクリックします。

むむ、エラーが記録されていました。

[ERROR] Runtime.ImportModuleError: Unable to import module ‘lambda_function’: No module named ‘PIL’ Traceback (most recent call last):

インポート文で指定したモジュールが見つからないようです。

Lambda上で追加のライブラリを使う場合は、そのライブラリのモジュールをLambda実行時にも参照できるように必要なファイルを追加する必要があるようです。

スポンサーリンク

次回、ライブラリを追加します

今回、実は初めてPythonを書いてみました。Javaならさんざん書いたのですが、やはり新しい言語となるとちょっと最初は時間かかりますね。スラスラとコード書けないのがもどかしい・・・(;・∀・)

疲れたので今回はここまでにします。次回、続き書きます。ではまた!

サーバレス開発は盛り上がってきています。是非、理解を深めて活用していきましょう。

左から基本編、SNSボット作り、中級編、最後は極める人向けのオライリー本です。

コメント

タイトルとURLをコピーしました