初めに
こんにちは。株式会社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つとして参考になれば幸いです。
それでは。