LoginSignup
13
8

More than 5 years have passed since last update.

高速に画像アップロードがされているように見せる簡単な方法

Posted at

Web アプリケーションで画像のアップロードはよくある機能です。その画像アップロードを高速にされているように見せることでユーザ体験を良くする(かもしれない)方法を紹介します。いたってよくある普通のアプローチですが、手軽にできるので参考にしてみてください。

なんでこんなことをするの?

最近のモバイル機器ではカメラ性能の向上と共にファイルサイズも向上しています。つまり回線環境が良かったとしてもアップロード時間がそれなりにかかり、さらにサーバサイドでリサイズ処理をしていたりすると、より時間がかかります。

そこで画面上にはアップロードする前に、画像を表示させておいて裏でアップロード処理を行う非同期処理にすることで、ユーザとしては高速にアップロードがされたというフィードバックを与えることができます。

実装例

今回は Vue.js を使ったコードでサンプルコードを紹介します。Vue.js 関係なく JavaScript の話なので、どんなフレームワークでも使えると思います。

普通にアップロードして画像を表示する

まずは普通に画像を選択して POST アップロードして表示するサンプルコードです。アップロードが完了すると JSON で画像の URL が返却されるので、それを表示します。アップロード中はインジケータのアニメーションを表示します。

1.gif

アニメーション GIF で動作イメージをキャプチャしました。これは 4G 回線 (上り 22Mbps) をシミュレーションし、約 2MB 程度の画像ファイルをアップロードしています。遅くはないけど、アップロードしているなという感じはあります。

<template>
  <div>
    <p><input type="file" @change="upload" accept="image/*" /></p>
    <indicator :loading="loading" />
    <dl v-if="image">
      <dt>アップロードした画像</dt>
      <dd><img :src="image"></dd>
    </dl>
  </div>
</template>

<script>
import axios from 'axios'
import Indicator from './Indicator'
export default {
  name: 'HelloWorld',
  components: {
    Indicator
  },
  data () {
    return {
      image: '',
      loading: false
    }
  },
  methods: {
    upload (e) {
      this.loading = true
      const file = e.target.files[0]
      const reader = new FileReader()
      reader.onload = (e) => {
        this.postData(e.target.result)
      }
      reader.readAsDataURL(file)
    },
    postData (image) {
      const params = new FormData()
      params.append('image', image)
      axios.post('http://0.0.0.0:9999/', params).then(res => {
        this.loading = false
        this.image = res.data.url
      })
    }
  }
}
</script>

<style scoped>
  dl, dt, dd {
    margin: 0;
    padding: 0;
  }
  img {
    width: 90%;
    margin: auto;
  }
</style>

すぐに画像を表示させる

次に画像を選択したときにもう表示をさせてしまって非同期でアップロード処理をし、アップロードが完了したら改めて差し替えるという方法に変更してみます。

2.gif

キャプチャを見てもらうとわかりますが高速に表示されているのがわかります。アップロードを待たずに表示させているので当然ですが、こちらのほうがユーザ体験が良くなるシーンはあると思います。

やっていることはシンプルで new FileReader() で画像ファイルを読み込んだ Base64 エンコーディングされた data: URL のリソースを当てているだけです。その後、POST した正規の URL に差し替えをしています。

<template>
  <div>
    <p><input type="file" @change="upload" accept="image/*" /></p>
    <indicator :loading="loading" />
    <dl v-if="image">
      <dt>アップロードした画像</dt>
      <dd><img :src="image"></dd>
    </dl>
  </div>
</template>

<script>
import axios from 'axios'
import Indicator from './Indicator'
export default {
  name: 'HelloWorld',
  components: {
    Indicator
  },
  data () {
    return {
      image: '',
      loading: false
    }
  },
  methods: {
    upload (e) {
      this.loading = true
      const file = e.target.files[0]
      const reader = new FileReader()
      reader.onload = (e) => {
        this.loading = false
        // ここで base64 された画像データを表示させる
        this.image = e.target.result
        this.postData()
      }
      reader.readAsDataURL(file)
    },
    postData () {
      const params = new FormData()
      params.append('image', this.image)
      axios.post('http://0.0.0.0:9999/', params).then(res => {
        // 改めて画像を差し替える
        this.image = res.data.url
      })
    }
  }
}
</script>

<style scoped>
  dl, dt, dd {
    margin: 0;
    padding: 0;
  }
  img {
    width: 90%;
    margin: auto;
  }
</style>

ただし、実際にはアップロードが完了しているわけではないのでブラウザを閉じたり、画面遷移するとアップロード処理が失敗するということがあります。採用する際には気をつけてください。

ブラウザを閉じる際にアラートを表示させるには beforeunload イベントを利用するといいかもしれません。

created () {
  window.addEventListener('beforeunload', (e) => {
    e.returnValue = ''
  })
}
13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8