Timer Camera Xで定点観測カメラを作る(その5)
前回からの続きです。
前回、前々回で以下の4を作ったので、今度こそ5を作ります。
- Node-REDでhttpで画像を送受信し、表示する環境を作る
- TimerCameraXで撮影した画像をPC上で確認する
- TimerCameraXからhttpでNode-REDに画像を送信する
- TimerCameraXをディープスリープさせて定期的に撮影するようにする
- AWS部分を作り、node-redから画像を送信する
- TimerCameraXとAWSをつなぐ
以下の図のAWS部分を作っていきます。
どこから作ろうかなと思うのですが、S3は使ったことがないので、S3から作っていきます。
AWSでルートユーザーで開発作業するのは怖いので、IAMユーザーを作っています。 今回、APIGateway、Lambda、S3を使うため、そのIAMユーザーが参加しているIAMグループに以下のポリシーをアタッチします。 ポリシーをアタッチする作業自体はルートユーザーで行います。
アタッチしたら、次はIAMユーザーでログインします。 ほぼ、以下の参考サイト通りの設定で作っていきます。
まずは、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の作成を選択します。 APIタイプはREST APIを選択します。 API名を入力し、作成します。 APIを作成したら、次にリソースを作成します。 アクションからリソースの作成を選択し、リソース名を入力します。 次にそのリソースを選択した状態で、メソッドを作成します。 メソッドはPOSTを選択し、チェックボタンを押します。 統合タイプはLambda関数を選択し、Lambda関数欄で上で作成したLambda関数を指定します。 これでほぼ完成です。
APIテストをしてみましょう。 以下の画面のテストボタンを押してみます。
リクエスト本文に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部分は完成です。 夜も遅くなってきたので、今回はここまでにします。
ここまでで部品はほぼできたので、次回は全て結合して、定点観測カメラを完成させたいと思います。