< Back

画像をアップロード前に圧縮する流行りの方法【Vue.js x Firebase x 令和】

こんにちは。年末年始はずっとFirebaseを触っていました @ykhirao です。たぶん3年くらい遅れて流行に乗り始めています。Typescriptお正月にはじめました。

今日は、仕事がお休みなのでスマホぽちぽちしていたらすごく参考になる記事を見かけましたのですが、こちらの記事 Firebaseで作る!リアルタイム画像変換CDN【Firebase Hosting + Cloud Functions】 - AppBrew Tech Blog で書かれている アップロードされた画像をそのまま表示する時代は平成とともに終わりを告げたわけですが[※要出典] という文言を見てくすっと笑ってしまい、少し前に 君はまだ平成のアーキテクチャを使ってるのか?僕はFirebaseと令和の時代に行くぞ。 という記事がたくさんの方に読まれていたこととか、Twitterの擬人化された #令和ちゃん を思い出したりして、エンジニアのみなさんは令和ちゃんもFirebaseも大好きっぽいので私も記事を書こうかなーーと思って書き始めました。ネタなんだけど誰かの役にたつ記事を書きたい次第です。

さて前置きが長くなりましたが、本文は、画像をアップロードする前にゴリッと処理する方法を書いて行きます。

画像をアップロード前に圧縮する流行りの方法

今回紹介すること、しないこと

# 画像アップロードについての手法

1. 画像処理をしない(平成)

2. 画像処理をする(令和)
  - 2.1 サーバーで処理する (https://tech.appbrew.io/entry/2020/01/15/120000 で紹介されている)
    - Firebase Extension(まだβっぽいですが公式の拡張があるっぽい!)
    - CloudFunctionsを自分でNode.JSごりごり書く
  - 2.2 クライアント側で処理する( <---- 今日の記事はここ!!!)

本文いらないからスクショとかGistみたいひと

以下のスクショのようになるGistはこちら
https://gist.github.com/ykhirao/1d36aeca9abb02709cc9dd3d676040ef

スクリーンショット 2020-01-16 13.32.29.png

スクリーンショット 2020-01-16 14.17.23.png

(最大 100x100 px にしているので2kbくらい)

処理して簡単に解説する

Canvasでごりっと処理するとか、ライブラリに頼るとか方法はあると思いますが今回は compressorjs を紹介したいっす。

component.js
import Compressor from 'compressorjs'

OR

index.html
<script src="https://cdn.jsdelivr.net/gh/fengyuanchen/compressorjs/dist/compressor.min.js"></script>

CDNはなかなか見つからなかったのですがぐぐったら このブログ で見つかった。まさとらんさんありがとうです。

画像は input タグで選択してもらって

<input
  type="file"
  id="file"
  multiple
  accept="image/*"
  @change="handleFiles"
/>

multiple で複数選択可にして accept で画像以外を一応弾いています。それで @change イベントのフックで画像を圧縮しています。

handleFiles(e: Event) {
  const images = this.images
  const files: FileList | null = (<HTMLInputElement>e.target).files
  if (files === null) return
  ;[...files].forEach((file: File) => {
    // 複数ファイルを受け入れているので、この中で処理している
  })
}

複数処理やfileが取得できなかった場合の処理は上のような感じで、Typescript的にエラーが出ていないのでこれでOKかなと思ってます(認識あってますか?)

const payload: Compressor.Options = {
  quality: 0.8,
  maxWidth: 100,
  maxHeight: 100,
  mimeType: 'image/jpeg',
  success(blob: Blob): void {
    // ここに成功時の処理を書く。次。
  },
  error(err: Error): void {
    console.log(err.message)
  }
}
new Compressor(file, payload)

実際の処理はこんな感じで横幅・縦幅の最大とか圧縮率とかたくさんの オプション があります。また成功時と失敗時は successerror のコールバックに書くみたいです。

success(blob: Blob): void {
},

サクセスのコールバックは Blob を受け取るのでFirebaseではこの段階でファイルのアップロードができます!!!ドキュメントはこちら。 Blobからアップロードできるとかすごくいいですね。(試してないけどたぶんそちらでUploadできる)

今回はアップロードする前に読み込みを確認したかったので、DataUrlという形式に変換しています。

success(blob: Blob): void {
  var reader = new FileReader()
  reader.onloadend = () => {
    const result = <string | ArrayBuffer | null>reader.result
    if (result instanceof ArrayBuffer || result === null) return
    images.push(result)
  }
  reader.readAsDataURL(blob)
},

images.push(result) する段階でDataUrlという形式の data:image/jpeg;base64,XXXXXXXXみたいな文字列になっています。ドキュメントの文字列からアップロードするを見ると

var message = 'data:text/plain;base64,5b6p5Y+344GX44G+44GX44Gf77yB44GK44KB44Gn44Go44GG77yB';
ref.putString(message, 'data_url').then(function(snapshot) {
  console.log('Uploaded a data_url string!');
});

これでアップロードできそうですね。

ドキュメントを読むと data:text/plain;base64, という文字列を削除したら ref.putString(message, 'base64url')でアップロードできそうです。たぶん。

サンプルファイルは <script lang="ts"> にしているのでVSCodeでひらいて、 CMD + Click とかで定義元とか飛んで行って自分で触ってみてください!!

スクリーンショット 2020-01-16 14.09.43.png

終わりに

ブラウザのAPIを叩くときにTypeScriptすごく開発体験良かったです。MDNを見に行かなくても何が返ってくるかわかるので、null制御とかそういうのがやりやすかったです。コード書いてて楽しかった。

なにか間違い見つけたら編集リクエストください!!!

最後まで読んでいただきありがとうございます。!!