0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

クロップした画像をCloud Storageにアップできなかった 〜HTMLCanvasElement.toBlob()でうまいことBlobを渡せなかった話〜

Last updated at Posted at 2021-08-07

input した画像を Firebase Storage に保存する実装をする方法を調べたら、大体 type = "file"の input タグから持ってくるみたいな見本が出てきますが、自分が触った実装は、少しイレギュラーなやつで、そこで詰まったことを書いときます。
フロントはVue.jsで、バックエンドはNode.jsの実装です。

結論としては、HTMLCanvasElement.toBlob()をうまいこと使えてなかっただけでした。

実現したいこと

自分がやった実装では、input から File を直接持ってくるんじゃなくて、vue-advanced-cropper っていうパッケージで、画像をクロップする必要がありました。

なので、getResult().canvasで返ってくる形式が File じゃなくて、 HTMLCanvasElement*1 って形式の Object だったので、それを頑張って Cloud Storage に保存できる形式にする必要がありました。

Firebase の Cloud Storage に保存できる Object の形式は、Blob、File、Uint8Array ってやつ、あとは、putString()ってメソッド使えばなんか文字列もいけるらしいです。詳しくは知りません[*2](#Firebase Cloud Storage)。

つまり、クロッパーで返ってきた HTMLCanvasElement を、上のどれかの形式にすればいいって話です。HTMLCanvasElement に、toBlob()ってメソッドがあったので、それで Blob にちゃちゃっと変換すればいいと思ったのですが、そこで詰まったのと解決法を書きます。

解決法

完成形見たい人はここ

失敗1

これはまあまあ論外のやつです。toString()的なやつと思いました。

async UploadFile(): void {
  const storageRef = firebase.storage().ref()

  // canvasElement(HTMLCanvasElement)の取得
  const canvasElement = this.$refs.cropper.getResult().canvas

  const blob = canvasElement.toBlob()

  await storageRef.child('path').put(blob)
}

Cloud Storage には、儚く「undifined」と書かれた9バイトのapplication/octet-streamファイルが上がっていました(Cloud Storage のデフォルトらしいです)。

失敗2

この辺からガチでわかんなくなりました。Blob自体は、toBlob()の引数のコールバック関数で生成されているのは分かったのですが、空の変数用意してってやったせいで、こんがらがりました。

async UploadFile(): void {
  const storageRef = firebase.storage().ref()

  // canvasElement(HTMLCanvasElement)の取得
  const canvasElement = this.$refs.cropper.getResult().canvas
  let blob

  canvasElement.toBlob((blobFromCanvasElement: Blob) => {
    blob = blobFromCanvasElement
  })
  await storageRef.child('path').put(blob) 
}

こっから、blob = {} as Blobとかでリテラル代入したり、どこで undifined になってるか typeof したり、なんか色々しましたが、完成形はめちゃくちゃ簡単でした。

完成形

コールバック関数内で済ませればいい話でした。

async UploadFile(): void {
  const storageRef = firebase.storage().ref()

  // canvasElement(HTMLCanvasElement)の取得
  const canvasElement = this.$refs.cropper.getResult().canvas

  await canvasElement.toBlob((blob: Blob) => {
     storageRef.child('path').put(blob) 
 )}
}

無事サトちゃんが Cloud Storage に保存されました!

なんかArray.prototype.forEach()のコールバック関数とかで、空のリストに追加していく実装をよく見てたので、頭がそれに縛られたミスっぽかったです。
Array.prototype.forEach()は undifined を返すけど、toBlob()の特徴として、「返り値がない*3」ってのがあって、それが原因なのかな…?

この記事が誰かの救いになれば :angel:

参考

HTMLCanvasElement

HTMLCanvasElement - Web API | MDN

HTMLCanvasElement.toBlob()

HTMLCanvasElement.toBlob() - Web API | MDN

Firebase Cloud Storage

ウェブでファイルをアップロードする | Firebase

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?