今回やること
AWSのストレージサービス(S3)アップロードした写真や画像ファイルから自動でサムネイル画像を作成してみようと思います。
このぐらいの簡単な処理ならEC2などでサーバー立ててプログラム実行環境を構築するより、今どきのサーバレスでプログラムだけ書いてささっと処理してしまった方がコストが安いですし、スマートですので、 AWS Lambdaを使ってやってみます。
準備
AWSのアカウントは無料でできますので、まだ作成していない方はこちらの記事でアカウントの作り方を紹介していますので、ご覧になってください。
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):
インポート文で指定したモジュールが見つからないようです。
次回、ライブラリを追加します
今回、実は初めてPythonを書いてみました。Javaならさんざん書いたのですが、やはり新しい言語となるとちょっと最初は時間かかりますね。スラスラとコード書けないのがもどかしい・・・(;・∀・)
疲れたので今回はここまでにします。次回、続き書きます。ではまた!
プログラミング初心者は、技術本より教材の方が学習効率がよいと思います
コメント
記事へのコメント失礼します。株式会社KICONIA WORKSの海保と申します。
画像加工に関して記事を書かれていたので、弊社で企画中のサービスについてインタビュー(web会議/謝礼あり)でご意見を頂きたくご連絡しました。
画像加工(フォーマット変換、サムネイリング、メディアごとの出し分け等)の開発・運用を、会社もしくは個人でしている人を探しているのですが、該当しますか?
(詳細はmailやSNS等でやりとりさせてください)
海保さん
コメントありがとうございます。
メールにて返信させていただきました。
ご確認お願い致します。