tAROの試行錯誤

技術的なことを色々試した過程を記録

Timer Camera Xで定点観測カメラを作る(その5)

前回からの続きです。

isogabamaware.hatenadiary.jp

前回、前々回で以下の4を作ったので、今度こそ5を作ります。

  1. Node-REDでhttpで画像を送受信し、表示する環境を作る
  2. TimerCameraXで撮影した画像をPC上で確認する
  3. TimerCameraXからhttpでNode-REDに画像を送信する
  4. TimerCameraXをディープスリープさせて定期的に撮影するようにする
  5. AWS部分を作り、node-redから画像を送信する
  6. TimerCameraXとAWSをつなぐ

以下の図のAWS部分を作っていきます。

f:id:tARO:20210124223326p:plain

どこから作ろうかなと思うのですが、S3は使ったことがないので、S3から作っていきます。

AWSでルートユーザーで開発作業するのは怖いので、IAMユーザーを作っています。 今回、APIGateway、Lambda、S3を使うため、そのIAMユーザーが参加しているIAMグループに以下のポリシーをアタッチします。 ポリシーをアタッチする作業自体はルートユーザーで行います。

f:id:tARO:20210131225815p:plain

アタッチしたら、次はIAMユーザーでログインします。 ほぼ、以下の参考サイト通りの設定で作っていきます。

qiita.com

まずは、S3のバケットをデフォルト設定で非公開で作成します。 S3の画面でバケットを作成を押して、バケット名を入力後、デフォルトでバケットを作成します。これでS3は完成です。

次に、LambdaでS3に画像を保存する部分を作ります。 LambdaをPython3.8で一から作成で作ります。 このままではS3にアクセスできないため、アクセス権限を変更します。 アクセス権限のタブから、実行ロールのロール名をクリックして、ロールの編集画面にいきます。 そこでポリシーをアタッチしますを押して、S3で検索して、AmazonS3FullAccessを選択し、ポリシーのアタッチを押します。 これで先程作成したLambdaからS3にアクセスできるようになりました。

次に、Python部分を実装してみます。 今回は、http postでjsonの中に'jpeg'という名前でbase64データが格納されているのを受け取ることになります。 event['jpeg']でその中身を取り出すことができます。 取り出したbase64データをデコードして、S3に書き込みます。 この辺りは上の参考サイトを参考にします。 ファイル名はひとまず、Lambdaが実行された現在時刻にします。 ここは後々撮影時刻に変更しようと思います。 参考サイトではput_objectのContentTypeが'image/png'でしたが、今回はjpegを保存するため、'image/jpg'に変更します。 あとは、例外発生時にエラーを返すように実装を追加します。 以下のように実装できました。

import boto3
import base64
import datetime

def get_now():
    now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))) # 日本時刻
    return now.strftime('%Y%m%d%H%M%S')

def convert_b64_string_to_bynary(s):
    """base64をデコードする"""
    return base64.b64decode(s.encode("UTF-8"))

def lambda_handler(event, context):
    try:
        base_64ed_image = event['jpeg']
        s3 = boto3.resource('s3')
        bucket = s3.Bucket('s3-timer-camera-x')
        now = get_now()
        bucket.put_object(
                        Key=f'{now}.jpg',
                        Body=convert_b64_string_to_bynary(base_64ed_image),
                        ContentType='image/jpg')
        return {'statusCode': 200}
    except:
        return {'statusCode': 400}

次にLambdaのテストを実行して、想定通りに動作しているかを確認します。 初めに、異常系のテストをします。入力ペイロードjpegがない場合に{'statusCode':400}を返すことを確認するためにテストを作成します。 Lambdaの右上の方にあるテストイベントの設定のところから入力となるjsonを作成します。 イベントテンプレートとして、hello-worldを選択し、そのまま作成します。 作成できたらテストボタンを押してみます。 実行結果が成功となり、戻り値が以下の通り、想定通りです。

{
  "statusCode": 400
}

次に、正しい入力が来たときにS3にjpegが書き込まれているかを確認してみます。 ここで(その1)で調べたjpgのbase64変換を行い、クリップボードへコピーし、それをテストに{jpeg:base64データ}となるようにbase64データを貼り付けます。

base64 sample.jpg | pbcopy

できたら、テストボタンを押してみます。 成功となりました。

{
  "statusCode": 200
}

正しくS3に保存されたかどうか、S3の方を見てみます。 jpgファイルが保存されているのでうまくいっています。 jpgファイルをダウンロードして、元のjpgを同じかどうか確認してみます。

これで、Lambda-S3部分はできました。

次は、API Gatewayを作ります。

API Gatewayの画面から、APIの作成を選択します。 APIタイプはREST APIを選択します。 API名を入力し、作成します。 APIを作成したら、次にリソースを作成します。 アクションからリソースの作成を選択し、リソース名を入力します。 次にそのリソースを選択した状態で、メソッドを作成します。 メソッドはPOSTを選択し、チェックボタンを押します。 統合タイプはLambda関数を選択し、Lambda関数欄で上で作成したLambda関数を指定します。 これでほぼ完成です。

APIテストをしてみましょう。 以下の画面のテストボタンを押してみます。

f:id:tARO:20210131230144p:plain

リクエスト本文にLambdaのテストと同じjsonを入力し、同じ動作をすることを確認します。 正常系で200、異常系で400が返ってきたらうまくいっています。 S3を見に行くと新しく現在時刻でjpg画像が保存されています。

このままデプロイすれば、PCからAPIGatewayにPOSTできるようになるはずですが、それでは世界中の誰からでもアクセスできてしまうため、簡単な認証っぽいものを作ります。

この辺りを参考にAPIキーを設定します。

https://qiita.com/baikichiz/items/ed787c5c79059213401e

デプロイまでできたら、curlを用いてAPIの動作確認を行います。 まずは、試しにPOSTしてみます。

curl -X POST -d "{\"key\":\"value\"}" https://~~~~~~
.execute-api.ap-northeast-1.amazonaws.com/test/image
{"message":"Forbidden"}

APIキーを設定していないので、{"message":"Forbidden"}が返ってきます。

続いて、APIキーを入力してみます。

curl -X POST -d "{\"key\":\"value\"}" https://~~~~~~
.execute-api.ap-northeast-1.amazonaws.com/test/image -H "x-api-key:....."
{"statusCode": 400}

jsonが間違っているので400が返ってきます。 続いて、key:valueの部分をlambdaのテストで使ったjpeg:base64データに変えて送ってみます。

curl -X POST -d "{\"jpeg\":\"/9j/4AAQSkZJR...\"}" https://~~~~~~~~.execute-api.ap-northeast-1.amazonaws.com/test/image -H "x-api-key:......."
{"statusCode": 200}

{"statusCode": 200}が返ってきました。 S3を見てみましょう。 新しくjpg画像が保存されています。成功です。

これでAWS部分は完成です。 夜も遅くなってきたので、今回はここまでにします。

ここまでで部品はほぼできたので、次回は全て結合して、定点観測カメラを完成させたいと思います。