BFT名古屋 TECH BLOG

日々の業務で得た知識を所属するエンジニアたちがアウトプットしていきます。

【API Gateway】API Gatewayを用いてLambda無しでS3に画像をアップロードする方法

初めに

こんにちは。株式会社BFT名古屋支店新人エンジニアのないとうです。
今回はVue.jsとAmazon API Gatewayを用いて、S3に画像をアップロードする方法について紹介したいと思います。
紹介する方法の概要は、パソコン内に保存されている画像を選択しAPIを呼び出すことでS3にアップロードするというもので、Lambdaは使いません。 f:id:bftnagoya:20211008142732p:plain
また今回は写真だけをS3に保存するのではなく「ステータスA」、「ステータスB」という2つの状態をファイル名を変更することで判断できるようにします。 (業務要件等により登録のステータスを登録したいとかの為に使用しました。本来はDBにいれたら良いと思いますが~。) この2つの状態は、先日投稿した記事にあるExif情報を取得するLambdaと組み合わせてDBに状態を保存するために必要なものです。
s3に画像をアップロードするだけなら必要はないので適宜修正してください。
(記事についてはこちら→【Lambda】【python】画像からEXIFデータを取得するLambdaを作成する - BFT名古屋 TECH BLOG

目次

前提条件

・画像を保存するS3のバケットが存在する
・Vue2のプロジェクトが存在する
(Vue.jsのプロジェクト作成についてはこちら→【Cognito】Vue.jsとCognitoを用いたログイン機能を実装する - BFT名古屋 TECH BLOG)
・IAMロールの作成をすることができる

実装環境

・Node.js : 12.22.3
・Vue.js : 4.5.13

API Gatewayの設定

APIに設定するロールの作成

1.以下の内容のポリシーを持つロールを作成する。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ListObjectsInBucket",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "【バケットのarn】"
        },
        {
            "Sid": "AllObjectActions",
            "Effect": "Allow",
            "Action": "s3:*Object*",
            "Resource": "【バケットのarn】/*"
        }
    ]
}

2.ロールに以下のような信頼関係を設定する

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

API の作成と設定

1.API Gatewayのコンソールから【APIの作成】をクリックする
f:id:bftnagoya:20211011165817p:plain
2.【REST API】の【構築】をクリックする
f:id:bftnagoya:20211011170709p:plain
3.【API名】を入力して、【APIの作成】をクリックする
f:id:bftnagoya:20211011171414p:plain 4.【アクション】を選択し【リソースの作成】をクリックする
f:id:bftnagoya:20211015114230p:plain 5.【リソース名】に「folder」、【リソースパス】に「{folder}」を記載し【リソースの作成】をクリックする
f:id:bftnagoya:20211013163516p:plain 6.4、5と同様の手順で、folderリソース上に【リソース名】が「object」、【リソースパス】が「{object}」のリソースを追加する
7.objectリソース上で【アクション】を選択し、【メソッドの作成】をクリックしputメソッドを作成する
f:id:bftnagoya:20211015114253p:plain 8.【統合タイプ】に「AWSサービス」を選択し、下記内容を選択または記述する
AWSリージョン】 :「ap-northeast-1」(s3のバケットのあるリージョンを選択する)
AWSサービス】  :「Simple Storage Service (S3)」
AWSサブドメイン】:「」(空白)
【HTTP メソッド】  :「PUT」
【アクションの種類】:「パスの上書を使用」
【パスの上書】   :「{bucket}/{key}」
【実行ロール】   :「【APIに設定するロールの作成】で作成したロールのarn」
【コンテンツ処理】 :「パススルー」
f:id:bftnagoya:20211014152602p:plain 9.【保存】をクリックしてputメソッドの設定を保存する
10.【putメソッド】の【統合リクエスト】を選択し【URLパスパラメータ】に以下の2つのパスを追加する
【名前】:「key」,【マッピング元】:「method.request.path.object」
【名前】:「bucket」,【マッピング元】:「method.request.path.folder」
11.サイドバーにある【設定】を選択し【バイナリメディアタイプ】に「*/*」と入力し【保存】をクリックする
f:id:bftnagoya:20211014161809p:plain
f:id:bftnagoya:20211014161819p:plain

画面の作成

App.vueを編集する

App.vueを下記内容に変更する

<template>
  <div class="upload">
      <h2><b>画像アップロード<b><h2>
      <form class="form">
      <div>
       <p>アップロード画像:</p>
        <input  type="file" ref="input" placeholder="ファイルを選択" @change="FILE" accept="image/jpeg" required multiple>
        <br><div class="warm">※複数画像選択の場合はCtrlを押しながら選択してください</div>
      </div>
      <p>状態:</p>
      <select class="col-md-3" v-model="select" >
        <option>ステータスA</option>
        <option>ステータスB</option>
      </select>
      </form>
      <button class="upload-button" @click="upload">アップロード</button>
  </div>
</template>

<script>

  //ライブラリのインポート

  import axios from 'axios'
 
  export default {
   name:"upload",
   //グローバル変数設定
   data(){
     return{
       upload_img: [],
       img_name: [],
       api_url: [],
       img_url: '',
       select: '',
       maxWidth:400,
       num:0,
     };
   },
    methods:{

      //ファイル取得関数//

      FILE(e) {

        var files = e.target.files || e.dataTransfer;
        this.upload_img=[];
        this.img_name=[];
        // ファイルが選択されたらリストに入れる
        for (var i = 0; i < files.length; i++) {
          this.upload_img.push(files[i]);
          this.img_name.push(files[i].name);

        }
      },

     //画像送信関数//

      putimage:async function(url,img){
       //送信処理
       await axios.put(
          //送信URL
          url,
          //送信画像
          img,
          //送信画像の情報
          {
            headers:{
              'content-type':'image/jpeg'
            }

          //送信成功時処理
          }).then(response =>{
             return response;

          //送信失敗時処理
          }).catch(e=>{
             alert(e);
        });
      },

     //複数画像送信関数//

      upload:async function(){
       
        if(this.select==""){

          alert("選択肢を入れてください");

        }else if(this.select=="ステータスA"){

          //inputされた画像分処理を繰り返しURLを作成する

          while(this.num==0){

            if(!this.img_name.length){
              break;
            }else{
              this.api_url.push("/v1/cdn-dis-explanatory-image/"+"0_"+this.img_name[0]);
              this.img_name.splice(0,1);
            }
          }
          //inputされた画像分送信処理を繰り返す

          while(this.num==0){
            if(!this.api_url.length){
              alert("ok");
              break;
            }else{
              await this.putimage(this.api_url[0],this.upload_img[0]);
              this.api_url.splice(0,1);
              this.upload_img.splice(0,1);
            }
          }
           //処理が終わったらリロードしてキャッシュを削除する
          this.$router.go({path: this.$router.currentRoute.path, force: true})

        }else if(this.select=="ステータスB"){

         //inputされた画像分処理を繰り返しURLを作成する

         while(this.num==0){
            if(!this.img_name.length){
              break;
            }else{
              this.api_url.push("/v1/cdn-dis-explanatory-image/"+"1_"+this.img_name[0]);
           }
        }
        //inputされた画像分送信処理を繰り返す
        while(this.num==0){
          if(!this.api_url.length){
            alert("ok");
            break;
          }else{
            await this.putimage(this.api_url[0],this.upload_img[0]);
            this.api_url.splice(0,1);
            this.upload_img.splice(0,1);
          }
        }
        //処理が終わったらリロードしてキャッシュを削除する
        this.$router.go({path: this.$router.currentRoute.path, force: true})
        }
      }
    }
  }
</script>

機能を確認する

1.コマンドでvueプロジェクトを起動する

$ npm run serve

2.ipにアクセスする
下の画像のように画面が表示され画像をアップロードした際にS3に画像が保存されれば完了
f:id:bftnagoya:20211014172631p:plain

おわりに

今回はAPI gatewayを用いてS3に画像を保存する方法を紹介しました。
API gatewayは通信時間が3秒を超えるとタイムアウトしてしまいます。
ですので、送信する画像のサイズが大きすぎる場合は、リサイズするなどの方法でサイズを小さくする必要があります。
画像を保存する方法の1つとして参考になれば幸いです。
それでは。


参照

aws.amazon.com

qiita.com