Vue.js で axios await/async を使って Azure Face API にファイルアップロードのデータを application/octet-stream で送るメモ

Vue.js で axios await/async を使って Azure Face API にファイルアップロードのデータを application/octet-stream で送るメモです。

Node.js で送る方法はこちら

今回は Vue.js でフロントエンドの JavaScript で行う方法です。もし、 Node.js で送る場合は以下を参考ください。

axios await/async で受信した画像を application/octet-stream でAzure Face APIに送るメモ

ともあれ、Array Bufferでデータを送るところは、そのまま参考にできます。

おもな仕組み

Vue.js でファイルアップロードの入力から、FileReaderクラスでデータを取り出します。さらにそこから、readAsArrayBuffer を使って ArrayBuffer としてデータを取り出します。

本来は onnload でリスナーを使って取り出しますが、Promiseでくるんで await / async で呼び出せるようにしています。

あとは、axios await/async で受信した画像を application/octet-stream でAzure Face APIに送るメモ で行っている流れと同じです。

ソースコード

実際に動くコードはこちらです。Bootstrap Vueを使って見た目をサッと作りやすいので、ブログ用にこうしています。

'Ocp-Apim-Subscription-Key':'Key' の部分の ‘Key’ を自分のキーに置き換えてください。

  • Windows 10
  • Google Chrome

で検証しています。

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vue.js FaceAPI as FileData</title>

  <!-- Load required Bootstrap and BootstrapVue CSS -->
  <link rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
  <link rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.css" />

  <!-- Load polyfills to support older browsers -->
  <script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver"
    crossorigin="anonymous"></script>

  <!-- Load Vue followed by BootstrapVue -->
  <script src="//unpkg.com/vue@latest/dist/vue.min.js"></script>
  <script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>

  <!-- Load the following for BootstrapVueIcons support -->
  <script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue-icons.min.js"></script>

</head>

<body>
  <div class="container">

    <div id="appFaceAPI-File">

      <div class="row">
        <div class="col">
            <h1>Vue.js FaceAPI as FileData</h1>
        </div>
      </div>

      <div class="row">
        <div class="col">
          <div class="form-group">
            <!-- https://bootstrap-vue.org/docs/components/form-file -->
            <div>
              <!-- Styled -->
              <b-form-file
                v-model="file"
                :state="Boolean(file)"
                placeholder="Choose a file or drop it here..."
                drop-placeholder="Drop file here..."
                @change="handlerFileChange"
              ></b-form-file>
              <div class="mt-3">Selected file: {{ file ? file.name : '' }}</div>
            </div>
          </div>
        </div>
      </div>
      
      <div class="row">
        <div class="col">
          <h2>response JSON Data : </h2>
          <pre><code>{{ response }}</code></pre>
        </div>
      </div>

    </div>

  </div>

  <!-- axios -->
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

  <script>
    const app = new Vue({
      el: '#appFaceAPI-File',
      data: {
        response: '',
        file: null
      },
      methods: {
        
        
        handlerFileChange: async function (e) {
          console.log('handlerFileChange');

          // 1. ファイルアップロードの入力から、まずファイル情報を取り出す
          // ここはファイル選択した瞬間に発動
          const files = e.target.files || e.dataTransfer.files;
          const file = files[0];

          // 2. ファイル参照の中から ArrayBuffer としてデータを取り出す
          let contentBuffer = await this.readFileAsync(file);
          // console.log(contentBuffer);

          this.sendCognitiveAsFile(contentBuffer);
        },

        // ファイル情報の中から ArrayBuffer としてデータを取り出す
        // 本来 onnload で取り出すが await / async で呼び出せるようにしている
        readFileAsync: function (file) {
          return new Promise((resolve, reject) => {
            let reader = new FileReader();
            reader.onload = () => {
              resolve(reader.result);
            };
            reader.onerror = reject;
            reader.readAsArrayBuffer(file);
          })
        },

        // このあたりは以下の記事を参考に。
        // https://www.1ft-seabass.jp/memo/2020/05/07/azure-face-api-application-octet-stream-using-axios/

        sendCognitiveAsFile: async function(contentBuffer) {
          // エンドポイント
          // 米国西部2の場合は West US 2 なので、 westus2.api.cognitive.microsoft.com
          //
          // 設定
          // returnFaceId true
          // recognitionModel detection_02
          // detectionModel detection_02
          const FACE_API_ENDPOINT_URL = 'https://westus2.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&recognitionModel=recognition_02&returnRecognitionModel=false&detectionModel=detection_02';

          // サブスクリプションをOcp-Apim-Subscription-Keyヘッダーに
          // 画像で送るのでContent-typeヘッダーにapplication/octet-stream指定
        
          const config = {
            url: FACE_API_ENDPOINT_URL,
            method: 'post',
            headers: {
              'Content-type': 'application/octet-stream',
              'Ocp-Apim-Subscription-Key':'Key'
            },
            data: contentBuffer
          };
 
          // axios
          try {
              // POSTリクエストで送る
              const responseAzure = await axios.request(config);
              console.log('post OK');
              // データ送信が成功するとレスポンスが来る
              console.log(responseAzure.data);
              //
              this.response = JSON.stringify( response.data, null , "\t");
          } catch (error) {
              console.log('post Error');
              // ダメなときはエラー
              console.error(error);
          }

        }

      }
      ,
      mounted() {
        console.log('mounted');
      }
    })
  </script>
</body>

</html>

そして、以下のよう動きます。

image

サブスクリプションキーが見えてしまうので対策の必要はあり

どこかにログインしているような場所でユーザーごとしか見れない状態であればこれでもいいですね。ただ、このまま誰でも見れる形で公開しするとサブスクリプションキーがソースコードで見えてしまうので、そのときは、なにかしらバックエンドで隠蔽したり対策の必要ですね。