echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【PowerApps】PowerAppsアプリで撮影した画像をS3バケットにアップロードする【Serverless Framework】

はじめに

Micorsoft の製品の一つ、Power Platform
中でもPower AppsやPower Automateは、ノーコーディング(またはローコーディング)でかなり本格的なアプリが簡単に作れる、画期的な製品です。

また簡単なアプリならコーディングなしでサクッと作れてしまうので、ITエンジニアにも便利だと思います。

そこでお試しに、(趣味と実用を兼ねて)「カメラで撮影した画像を、AWSのS3バケットに保存する」アプリを作成しました。

なおPower Platformについては、「Power BI 王子」こと清水優吾さんが、素晴らしい資料を公開してますので、ご参照ください。

www.slideshare.net

使うもの

  • Power Apps
    • 上で説明した通りです。
  • カスタムコネクタ
    • Power Platform製品の「コネクタ」(=他のアプリとの連携用の部品)でサポートされていないアプリ・サービスとも連携を可能にする機能。
    • 主に、地先APIを始め、外部Web APIのコールなど
  • AWS Lambda & AWS S3
    • S3にファイルを保存する関係で使います。
  • Serverless Framework
    • もちろん今回も使います。

【注意】カスタムコネクタについて

カスタムコネクタですが、Power Appsで使用する場合「Premium限定機能」となります。(ダイアモンドのアイコンが表示されている)
そのため、試用は可能ですが、一定期間が過ぎると使用不可能(またはサブスクリプションの購入が必要)なので、注意してください。

※あくまで「Power Platformの色々な機能のお試し」として読んでください。

Power Apps側の作成

f:id:Makky12:20200301173858p:plain f:id:Makky12:20200301173941p:plain

Power Apps側の機能ですが、こんな感じになってます。(「Kintai-Upload」は、なんとなく付けただけで、意味はないです)

  • 「撮影」画面で、カメラ機能を使って撮影する。(撮影すると自動で「確認」画面に遷移する)
  • 「確認」画面で「送信する」をクリックすると、S3バケットに撮影した画像をアップロードする。
  • 「やりなおす」をクリックすると、「確認」画面に戻る

で、上記機能の実装方法なんですが、変に説明をするより、マイクロソフトの吉田大貴さんのこの動画を見た方が早いですので、そちらを参照ください。
マジでこの動画を参考にすれば、画像をS3にアップロードする直前までできてしまいます。

www.youtube.com

注意点としては、以下の通りです。

# JSON([画像のバイナリデータ], JSONFormat.IncludeBinaryData) とすると、バイナリデータをBase64形式の文字列に変換できる。    
# [画像のバイナリデータ]には「Image1.Image」など、「画像の表示部品の名前.Image」を指定する。
JSON(Image1.Image, JSONFormat.IncludeBinaryData)

AWS側の設定について

今回、カスタムコネクタ作成にはOpen API(swagger)の定義を利用します。
またOpen API定義はAPI Gatewayからエクスポートできるので、先にAWS側のリソースを作成する必要があります。

というわけで、今回もServerless Frameworkの出番です。

...といっても、Serverless Frameworkは過去記事でもかなり扱ってるので、今回はserverless.ymlの内容だけ。

service: kintai-upload
  
provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region: ap-northeast-1
  stackName: ${self:service}
  apiName: api-${self:service}
  profile: default
  
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "s3:ListBucket"
        - "s3:PutObject"
        - "s3:GetObject"
      Resource:
        - arn:aws:s3:::${self:service}/*  
  
  environment:
    TZ: Asia/Tokyo
  
functions:
  CreateImage:
    handler: createImage.index
    events:
      - http:
          path: /create
          method: post
          request:
            parameters:
              body:
                imagebinary: false
    environment:
      BUCKET_NAME: ${self:service}
  
  SendSms:
    handler: sendSms.index
    events:
      - s3:
          bucket: ${self:service}
          event: s3:ObjectCreated:*
    environment:
      BUCKET_NAME: ${self:service}

ざっくりまとめると、下記の流れです。(「SendSms」Lambda自体は、今回の記事では未使用)

  • api-kintai-uploadという名前のAPI Gatewayを作って、
  • そこに「CraeteImage」Lambdaを実行するエンドポイントを作成して、
  • 「kintai-upload」という名前のバケットを作成して、そこに「createObject」トリガを定義して
  • 上記トリガで「SendSms」Lambdaを実行する

なお「resourceセクションへのS3バケットの定義(てか、そもそもresourceセクション自体)がないけど」と思ったかもしれませんが、「function」セクションの「events」で「s3」を定義するだけで、下記設定を自動で行ってくれますので、「resources」セクションの定義は不要です。

  • events.s3.bucket に指定した名前のバケット作成
  • events.s3.event に定義したイベントトリガの作成

カスタムコントロールの作成

で、肝心のカスタムコネクタの作成ですが、これはPower Appのホーム画面から[データ]-[カスタムコネクタ]を選択し、右上の「カスタムコネクタの新規作成」をクリックすればOKです。

なお、作成方法がいくつかありますが、今回は「OpenAPIファイルをインポート」で実行します。 f:id:Makky12:20200307085858p:plain

OpenAPIファイルの取得

OpenAPIファイルですが、API Gatewayの場合、該当のAPIを選択した後、下記手順で取得できます。

  • 左のリストから「ステージ」を選択  
  • 「ステージ」で該当ステージを選択後、「エクスポート」タブを選択
  • 「次の形式を選択」で「Swagger」を選択後、ファイル形式に「JSON」を選択する。
    • OpenAPI3、及びYAML形式はまだサポートしてません。

f:id:Makky12:20200307091646p:plain

ファイルをDLしたら、「カスタムコネクタの作成」の「OpenAPIファイルをインポートします」で、DLしたファイルを選択します。
また「コネクタ名」に、一意の名前を入力します。
f:id:Makky12:20200307091806p:plain

で、あとは「全般」「セキュリティ」「定義」などを設定しますが、ここからは「swaggerエディタ」をONにすれば直接OpenAPIファイルの内容を編集できますので、そちらで編集します。
f:id:Makky12:20200307091905p:plain

今回は。こんな感じで定義しました。

swagger: '2.0'
info: {version: '2020-02-29T10:45:32Z', title: api-kintai-upload-to-cloud}
host: *******.amazonaws.com
basePath: /dev
schemes: []
consumes: []
produces: []
paths:
  /create:
    post:
      responses:
        '200': {description: OK}
        '400': {description: Request Error}
        '500': {description: Internal Error}
        default: {description: Something Error}
      parameters:
      - name: imagebinary
        in: body
        description: kintai file binary
        required: true
        schema: {type: string}
      operationId: Kintai-upload-to-cloud-makky12
      description: Kintai-upload-to-cloud用のカスタムコネクタ
      summary: Kintai-upload-to-cloud用のカスタムコネクタ
definitions: {}
responses: {}
securityDefinitions: {}
security: []
tags: []

ちなみに、テストをする場合「テスト」タブで必要な値を入力すればOKです。
(今回の場合、「imagebinary」にbase64文字列に変換した画像のバイナリデータを入力する) f:id:Makky12:20200304201223p:plain

Power Appsに組み込む

で、作成したカスタムコネクタをPower Appsに組み込む方法ですが、これは左のツリーの[データソース]-[コネクタ]から、先程作成したカスタムコネクタを選択すればOKです。

そして、実際にAPIにリクエストを送るには、ボタンの「onSelect」イベントなどで、下記のように設定すればOKです。

# 「api-kintai-upload-to-cloud」は、カスタムコネクタ名。    
# 「Kintaiuploadtocloudmakky12」は、OpenAPIの「operationId」の値。(「定義」タブの「操作ID」で設定可能)  
# imageBase64は、base64文字列に変換した画像のバイナリデータ
'api-kintai-upload-to-cloud'.Kintaiuploadtocloudmakky12(imageBase64);  

S3バケットに保存する。

ここまでこれば、あとはLambda側の処理です。

Lambda側では、特に特別なことはしていません。
よく見かける、画像をs3バケットに保存する処理だと思います。

強いて言えば、注意するのは下記2点です。

  • JSON()関数でbase64文字列にした場合、先頭と末尾に「"」がついているので、「"」を除去する
  • その他、「data:image/png;base64,」という文字列も付与されるので、これを取り除く
// @ts-check
'use strict';

const AWS = require("aws-sdk");
const S3 = new AWS.S3();
const moment = require("moment");

module.exports.index = async event => {
  
  let statusCode = 200;
  let message = "";
  
  try {
    console.info(`[even] ${JSON.stringify(event)})`);
    console.info(`[event body] ${event.body})`);  
  
    const bodyReplaced = event.body.replace(/\"/g, "").replace("data:image/png;base64,", "");
    console.info(`[event replaced] ${bodyReplaced})`)  
  
    const timeStamp = moment().format("YYYYMMDDHHmmssSSS");
    const params = {
      Bucket: process.env.BUCKET_NAME,
      Body: Buffer.from(bodyReplaced, 'base64'),
      Key: `kintai_${timeStamp}.png`,
      ContentType: "image/png"
    };
  
    const data = await S3.putObject(params).promise();
    // console.info(`[S3 data] ${JSON.stringify(data)}`);
  } catch (error) {
    statusCode = 500;
    message = error.message;
    console.error(`[error Message] ${message}`);
  }
  
  return {
    statusCode: statusCode,
    body: JSON.stringify({ message: message }),
  };
};
  

結果

「送信する」ボタンをクリックすると、S3バケットに画像ファイル(*.png)が保存されます。 f:id:Makky12:20200307193524p:plain

そして、ダウンロードして画像ビューワーで見ると、問題なく表示できます。 f:id:Makky12:20200307193617p:plain

まとめ

以上、カスタムコネクタを使用して、画像をS3バケットにアップロードする方法でした。

カスタムコネクタはプレミアム限定機能ですが、こういう感じで、自作APIなどの操作をローコーディングで行えます。
また、自作APIでなくても、プレミアム限定でなくても使用できるAPIもありますので(Twitterとか)、非常に便利です。

Power AppsやPower Automateなど、Power Platform製品、なかなか便利なので、今後も使っていきたいです。

それではまた。