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」ってのがあって、それが原因なのかな…?
この記事が誰かの救いになれば
参考
HTMLCanvasElement
HTMLCanvasElement - Web API | MDN
HTMLCanvasElement.toBlob()
HTMLCanvasElement.toBlob() - Web API | MDN