1

I've got a Javascript native Blob of a jpeg and I'd like to store it as a firestore blob.

But of course Firestore says "FirebaseError: Function DocumentReference.update() called with invalid data. Unsupported field value: a custom Blob object (found in field image in document..."

Is there any way to convert from javascript-blob to firestore-blob?

canvas.toBlob(async (blob)=> {
    console.info(`Converting to blob: ${blob.constructor.name} and storing to firestore...`);
    await myselfRef.update({
        image: blob
    }, 'image/jpeg', 0.4));
Benjamin H
  • 5,164
  • 6
  • 34
  • 42

1 Answers1

3

To convert a JavaScript Blob to a Firestore Blob you have to follow the steps below:

  1. Convert JavaScript Blob to ArrayBuffer using your_blob.arrayBuffer().
  2. Wrap the ArrayBuffer you just got into a Uint8Array by passing it as the single parameter to the constructor.
  3. Use the firebase.firestore.Blob.fromUint8Array(your_array) method to, finally, create the Firestore Blob.

If you want to perform the inverse transformation, from Firestore Blob to JavaScript Blob, you have to, pretty much, do the opposite:

  1. Convert Firestore Blob to Uint8Array using firestore_blob.toUint8Array().
  2. Create the JavaScript Blob using the constructor using the array. Keep in mind that you have to invoke it using a list of arrays, e.g., new Blob([my_array]).

Here is an example of this in practice. The function save() is used to, well, save the contents of the canvas to Firestore. And, the function load() loads them into the canvas again.

const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
const db = firebase.firestore();

function save() {
  canvas.toBlob(async blob => {
    const blob_url = URL.createObjectURL(blob);
    const ref = db.collection('blob').doc('blob');
    const array_buffer = await blob.arrayBuffer();
    const uint8_array = new Uint8Array(array_buffer);
    const firebase_blob = firebase.firestore.Blob.fromUint8Array(uint8_array);
    ref.update({
      image: firebase_blob,
    });
  });
}

async function load() {
  const ref = db.collection('blob').doc('blob');
  const doc = await ref.get();
  const firebase_blob = doc.get('image');
  const uint8_array = firebase_blob.toUint8Array();
  const blob = new Blob([uint8_array]);
  const blob_url = URL.createObjectURL(blob);
  const image = new Image();
  image.onload = () => {
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(image, 0, 0);
    URL.revokeObjectURL(blob_url);
  }
  image.src = blob_url;
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
frangaren
  • 498
  • 3
  • 9
  • Not sure this is cross-browser compatible as `Blob.arrayBuffer` doesn't seem to be supported in either Safari or Firefox Android at the moment – som Feb 01 '21 at 04:22
  • Following up the previous comment and some testing ... despite what the docs say it appears that the Response API enjoys support across all modern browsers (including FF Android 85, currently listed as unsupported): `await new Response(blob).arrayBuffer()` see https://stackoverflow.com/a/55204517/720204 – som Feb 01 '21 at 05:09
  • I'm getting "Cannot read properties of undefined (reading 'toUint8Array')" at `firebase_blob.toUint8Array()`. Is `toUint8Array()` replaced with something else? – bot1131357 Feb 24 '23 at 13:57