API Gateway+Lambdaで画像を送信して処理する

9月 17, 2022

AWSのLambdaで画像を扱う機会があったのですが、HTTPで送信した画像形式の理解が浅く、かなりはまったのでこちらにまとめておきます。

結論:base64エンコード+JSON形式で送る

結論から書くと画像をBase64形式にしてJSON形式で送るのが良さそうでした。

import json
import requests
import base64

image = "sample_0.jpeg"
data = open(image, 'rb').read()
encoded_data = base64.b64encode(data).decode('utf-8')

url = 'https://***.ap-northeast-1.amazonaws.com/***'
response = requests.post(url, data=json.dumps({'file': encoded_data}))

requestsモジュール上で指定できる「data」変数は画像ファイルを読み込ませることもできるのですが、その設定では受け取り先のLambdaで画像データだけを抽出することができませんでした。そのため、上記のようにjson形式にして、1つのkeyがまるまる画像データとなっている構造にすることで問題を解決しています。

以下で詳細な手順を説明していきます。

Lambdaの準備

まず初めにLambda関数を作成します。「関数の作成」を押して、「一から作成」で関数名を適当に設定し、今回はランタイムはPythonを選択します。他はデフォルトでOKです。

無事作成できれば下記の画面のように関数の概要に作成したlambda関数が表示されているはずです。

一旦lambdaの設定はこちらで終了です。

API Gatewayの準備

次にAPI Gatewayを作成していきます。「APIを作成」をクリックし、REST APIを選択します。

プロトコルを「REST」にし、「新しいAPI」を選択します。API名は自由に決めてください。

APIの設定画面で、「アクション」を選択し、「メソッドの作成」をクリックします。今回は画像を送るので「POST」で作成してください。

POSTのセットアップ画面は下記のように設定します。今回はLambdaと連携するため、統合タイプはLambda関数とし、「Lambdaプロキシ統合の使用」にチェックを入れます。「Lambdaリージョン」は自分がLambda関数を作成するリージョンを設定してください。

Lambda関数は先ほど自身で作成したLambda関数の名前を設定します。

アクションをクリックし、「APIのデプロイ」を押します。

下記のデプロイ設定画面が出てくるので、適当なステージ名を設定して、「デプロイ」を押下します。

完了すると下記のようなAPI接続のためのURLが表示されるはずです。これでAPI Gatewayの設定は終了です。

画像処理テスト用のlambdaコードの作成

Lambdaの画面に戻り、「lambda_function.py」を編集します。

import json
import base64

def lambda_handler(event, context):
    #event['body']自体がbase64エンコードされているのでデコードしてjsonとして読み込み
    params = json.loads(base64.b64decode(event['body']))
    #bodyの中身はjson形式です。'file'キーについてはこの後出てくるリクエストコード内で定義します
    encoded_data = params['file']
    #取り出したデータはそのままでは文字列型なので、UTF-8でencodeしてバイト列に戻し、その後base64でデコードすることで元の画像フォーマットに変換します
    image_data = base64.b64decode(encoded_data.encode("utf8"))
    print(image_data[:20])#①上手く画像フォーマットに変換できていれば後述の②と同じ結果となる
    return {
        'statusCode': 200
    }

「Deploy」ボタンを押してデプロイします。これでLambda側の準備も完了です。

リクエスト用のコード作成

最後に画像をリクエストするコードを作成します。

import json
import requests
import base64

image = "sample_0.jpeg"
data = open(image, 'rb').read()
print(data[:20])#②正しく送信できれば、上述のlambdaコード①と同じデータが得られる
#画像データをbase64でエンコードし、その後UTF-8形式で文字列に変換する(jsonではbyte型を扱えないため)
encoded_data = base64.b64encode(data).decode('utf-8')
url = 'https://***.ap-northeast-1.amazonaws.com/***' #API Gateway作成時に取得したもの
response = requests.post(url, data=json.dumps({'file': encoded_data}))

いかがでしたでしょうか。API Gateway + Lambdaの連携はよく使われるにも関わらずハマりポイントが多いので、気をつけて使っていきたいですね。それではまた。