初めに
こんにちは。株式会社BFT名古屋支店新人エンジニアのないとうです。
今回はVue.jsとAmazon API Gatewayを用いて、S3に画像をアップロードする方法について紹介したいと思います。
紹介する方法の概要は、パソコン内に保存されている画像を選択しAPIを呼び出すことでS3にアップロードするというもので、Lambdaは使いません。
また今回は写真だけを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の作成】をクリックする
2.【REST API】の【構築】をクリックする
3.【API名】を入力して、【APIの作成】をクリックする
4.【アクション】を選択し【リソースの作成】をクリックする
5.【リソース名】に「folder」、【リソースパス】に「{folder}」を記載し【リソースの作成】をクリックする
6.4、5と同様の手順で、folderリソース上に【リソース名】が「object」、【リソースパス】が「{object}」のリソースを追加する
7.objectリソース上で【アクション】を選択し、【メソッドの作成】をクリックしputメソッドを作成する
8.【統合タイプ】に「AWSサービス」を選択し、下記内容を選択または記述する
【AWSリージョン】 :「ap-northeast-1」(s3のバケットのあるリージョンを選択する)
【AWSサービス】 :「Simple Storage Service (S3)」
【AWSサブドメイン】:「」(空白)
【HTTP メソッド】 :「PUT」
【アクションの種類】:「パスの上書を使用」
【パスの上書】 :「{bucket}/{key}」
【実行ロール】 :「【APIに設定するロールの作成】で作成したロールのarn」
【コンテンツ処理】 :「パススルー」
9.【保存】をクリックしてputメソッドの設定を保存する
10.【putメソッド】の【統合リクエスト】を選択し【URLパスパラメータ】に以下の2つのパスを追加する
【名前】:「key」,【マッピング元】:「method.request.path.object」
【名前】:「bucket」,【マッピング元】:「method.request.path.folder」
11.サイドバーにある【設定】を選択し【バイナリメディアタイプ】に「*/*」と入力し【保存】をクリックする
画面の作成
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に画像が保存されれば完了
おわりに
今回はAPI gatewayを用いてS3に画像を保存する方法を紹介しました。
API gatewayは通信時間が3秒を超えるとタイムアウトしてしまいます。
ですので、送信する画像のサイズが大きすぎる場合は、リサイズするなどの方法でサイズを小さくする必要があります。
画像を保存する方法の1つとして参考になれば幸いです。
それでは。