[アップデート] AWS Amplify for JavaScript がS3のリジューム可能なファイルアップロードをサポートしました

2021.11.08

こんにちは、リサリサです。

AWS Amplify for JavaScript がストレージのリジューム可能なファイルアップロードをサポートしたので触ってみました。

※フロント全然分からない人間が書いています。vue.js 今回初めて触りました…。なので、ソースコード等、細かい部分はあまり当てにしないで頂けると幸いです。あくまでアップデートした機能の動きの参考と見て頂ければと思います。

追加された事

AWS Amplify for JavaScript がストレージのリジューム可能なファイルアップロードをサポート

AWS Amplify for JavaScript は、Amplify ストレージカテゴリ経由で Simple Storage Service(Amazon S3) にファイルをアップロードする際の一時停止、再開、キャンセルのアクションに対応しました。Amplify はユースケース指向の UI コンポーネント、ライブラリ、コマンドラインツールのセットを提供し、フロントエンドのウェブやモバイルのデベロッパーがアプリケーションのための AWS クラウドバックエンドを簡単に構築できるようにします。

S3 アップロードの一時停止、再開、キャンセルが出来るようになったようです。また、途中で通信が途切れた場合なども、途中から再開が出来るようです。

const upload = Storage.put(file.name, file, {
  resumable: true,
});

//一時停止
upload.pause();

//再開
upload.resume();

//キャンセル
Storage.cancel(upload);

やってみた

S3 にアップロードできるアプリを作る

以下ブログを元にS3にアップロードができるアプリケーションをまず作ってみます。

基本的にはブログ通りに進めるのですが、少し古い記事で、サンプルソースもあまり更新がされておらずライブラリが古めです。サンプルソースのままだと amplify のバージョンが古くて今回追加されたメソッドが使えません。なので、試される方は git からcloneした後、package.json の dependencies を以下に書き換えてから「yarn install」してみてください。

  "dependencies": {
    "@vue/cli-plugin-unit-jest": "^3.2.0",
    "@vue/cli-service": "^3.2.0",
    "@vue/test-utils": "^1.0.0-beta.27",
    "aws-amplify": "^4.3.4",
    "aws-amplify-vue": "^2.1.5",
    "aws-sdk": "^2.1022.0",
    "e": "0.0.4",
    "element-ui": "^2.15.6",
    "fsts": "^0.0.39",
    "vue": "^2.5.16",
    "vue-router": "^3.0.1",
    "vuex": "^3.0.1"
  },

ブログ通りに進め、ユーザー作成までするとこのような画面になります。

S3 アップロード、ダウンロード、削除ができるアプリが完成です。

一時停止、再開、キャンセルの機能を追加

ここに、一時停止、再開、キャンセルの機能を追加して、動作確認してみます。

src/Home.vueを以下のように変更します。

pause、resume、cancel、というボタンを追加して、それぞれのメソッドを追加しています。また、「progressCallback」で、どこまで送信したかを監視していきます。

<template>
<div class="container shifted">
    <h1 class="h1">
      S3 Objects
    </h1>
    <el-button>
      <label for="file">
        Upload
        <input type="file" @change="upload" id="file" style="display:none;">
      </label>
    </el-button>
    <el-button @click="pause">pause</el-button>
    <el-button @click="resume">resume</el-button>
    <el-button @click="cancel">cancel</el-button>
    <el-button @click="refresh" class="el-icon-refresh-left"></el-button>
    <el-table :data="s3Data" style="width: 100%">
      <el-table-column prop="key" label="Key" sortable>
      </el-table-column>
      <el-table-column prop='lastModified' label="LastModified" sortable>
      </el-table-column>
      <el-table-column prop="size" label="Size" sortable>
      </el-table-column>
      <el-table-column>
        <template slot-scope="scope">
          <el-button @click="download(scope.row)">Download</el-button>
          <el-button type="danger" @click="openDeleteDialog(scope.row)">Delete</el-button>
        </template>
      </el-table-column>    
    </el-table>
    <el-dialog title="Delete objects" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
      <span>{{ deleteObject }} Objects will be deleted</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">Cancel</el-button>
        <el-button type="primary" @click="deleteOK()">Confirm</el-button>
      </span>
    </el-dialog>
  </div>
</template></pre>
<pre><script>
import Vue from 'vue'
import Amplify, { API,Storage } from 'aws-amplify';
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/en'

Vue.use(ElementUI, { locale })

export default {
  name: 'Home',
  data () {
    return {
      s3Data: [],
      dialogVisible: false,
      selectedRow: "",
      deleteObject: "",
      uploadObject:""
    }
  },
  created () {
    this.refresh()  
  },
  methods: {
    refresh () {
      Storage.list('')
        .then(result => this.s3Data = JSON.parse(JSON.stringify(result)))
        .catch(err => console.log(err));
    },
    // 一時停止
    pause(){
      console.log(`★${new Date().toLocaleString()}_転送中断`) this.uploadObject.pause()
   },
    // 再開
    resume(){
      console.log(`★${new Date().toLocaleString()}_転送再開`)
      this.uploadObject.resume()
    },
    // 中断
    cancel(){
      console.log(`★${new Date().toLocaleString()}_転送中止`)
      Storage.cancel(this.uploadObject);
    },
    upload(e) {
      var files = e.target.files || e.dataTransfer.files
      console.log(`★${new Date().toLocaleString()}_転送開始`)
      this.uploadObject = 
      Storage.put(files[0].name, files[0],{
        resumable: true,
        progressCallback: (progress) => {
          console.log(`★${new Date().toLocaleString()}_${progress.loaded}/${progress.total}送信済...`)
          if(progress.loaded == progress.total){
            console.log(`★${new Date().toLocaleString()}_転送終了`)
          }
        }
      })

    },
    download (row) {
      Storage.get(row['key'], { download: true })
        .then(result => {
          console.log(result)
          const url = URL.createObjectURL(new Blob([result.Body]));
          const link = document.createElement('a')
          link.href = url
          link.download = row['key']
          console.log(link)
          link.click()
        })
        .catch(err => console.log(err));
    },
    openDeleteDialog(row) {
      this.selectedRow = row;
      console.log(row)
      this.deleteObject = row['key'];
      this.dialogVisible = true
    },
    deleteOK () {
      this.dialogVisible = false
      Storage.remove(this.selectedRow['key'])
        .then(result => {
          console.log(result)
          this.refresh() 
          }
        )
        .catch(err => console.log(err));
    },      
    handleClose(done) {
        this.$confirm('Are you sure to close this dialog?')
          .then(_ => {
            done();
          })
          .catch(_ => {});
    }
  }
}

</script>

<style>
label {
  color: #606266;  
  background-color:white;
  padding: 10px;
}
</style></pre>
<pre>

画面はこんな感じになりました。

動かしてみる

Chrome の F12 でログを確認しながらそれぞれの動きを確認してみます。デバッグログがたくさん出るので、「★」でフィルターして確認してます。

一時停止、再開、キャンセル

「Upload」からアップロードを始めると、転送が始まります。監視ログでどのくらい転送が進んだかが分かります。

「pause」で中断すると、転送も中断し監視ログも止まりました。「resume」から再開をすると「120586240/1073741824」となり、途中から再開されているのがかわります。

★2021/11/8 16:56:14_転送開始
★2021/11/8 16:56:14_5242880/1073741824送信済...
★2021/11/8 16:56:15_10485760/1073741824送信済...
★2021/11/8 16:56:15_15728640/1073741824送信済...
~略~
★2021/11/8 16:56:16_104857600/1073741824送信済...
★2021/11/8 16:56:16_110100480/1073741824送信済...
★2021/11/8 16:56:17_115343360/1073741824送信済...
★2021/11/8 16:56:17_転送中断
★2021/11/8 16:56:20_転送再開
★2021/11/8 16:56:20_120586240/1073741824送信済...
★2021/11/8 16:56:20_125829120/1073741824送信済...
★2021/11/8 16:56:20_131072000/1073741824送信済...
~略~
★2021/11/8 16:56:22_214958080/1073741824送信済...
★2021/11/8 16:56:22_220200960/1073741824送信済...
★2021/11/8 16:56:22_225443840/1073741824送信済...
★2021/11/8 16:56:22_転送中止

強制終了からの再アップロード

ある程度送信した後、画面をリフレッシュして、通信を強制切断してみます。その後、「Upload」から再度アップロードボタンからアップロードをしてみます。

最初から「466616320/1073741824」なので、最初に送信した分は無駄にならず、途中から再開されているのが分かります。

★2021/11/8 17:06:50_転送開始
★2021/11/8 17:06:50_466616320/1073741824送信済...
★2021/11/8 17:06:51_471859200/1073741824送信済...
★2021/11/8 17:06:51_477102080/1073741824送信済...
★2021/11/8 17:06:51_482344960/1073741824送信済...
★2021/11/8 17:06:51_487587840/1073741824送信済...

最後に

amplify 初めて触ったのですが、あっという間にリソースが作成されて感動しました。普段触らない機能を触ると楽しいですね。どなたかのお役に立てれば幸いです。

参考

[1]ストレージ-ファイルのアップロード-JavaScript-AWSAmplify Docs

[2]AWS Amplify for JavaScript がストレージのリジューム可能なファイルアップロードをサポート