vincent O
vincent O

Reputation: 538

Uploading an array of images to firebase storage

I am trying to upload an array of images to firebase storage. I have written a function for single image upload and it works perfectly, but i cannot seem to wrap my head around how I can implement image array upload. Can someone kindly point me in the right direction. Below is my sample, and I have kept only the relevant part

        uploadAsync = async uri => {
          const user = Firebase.auth.currentUser.uid;
          const path = `users/${user}/images/${Math.round(
                  Math.random() * 1000000000
              )}.jpg`;

          return new Promise(async (res, rej) => {
            const response = await fetch(uri);
            const file = await response.blob();

            const upload = Firebase.storage().ref(path).put(file);

            upload.on(
              'state_changed',
              snapshot => {},
              err => {
                rej(err);
              },
              async () => {
                const url = await upload.snapshot.ref.getDownloadURL();
                res(url);
              }
            );
          });
        };

        updateImage = async ({ image }) => {
          const user = Firebase.auth.currentUser.uid;
          for (let i = 0; i < image.length; i++) {
            const file = image[i].image;
            const remoteUri = await this.uploadAsync(file);
            const firestoreRef = firebase.firestore().collection('Images').doc(user);
            firestoreRef.set({
              image: [remoteUri]
            });
          }
        }

Upvotes: 0

Views: 1826

Answers (2)

Tobias Sch&#228;fer
Tobias Sch&#228;fer

Reputation: 1358

Basically the issue here is that you need to upload every image on its own. A "bundle" upload or similar does not exist.

The normal approach

The basic approach to such a propblem would be looping over the array (here array of images imgArr) and upload each item individually.
Such a function can look like this:

for (i = 0; i < imgArr.length; i++) {
    this.updateImage(imgArr[i]);
}

or with the forEach method:

imgArr.forEach((img) => {
    this.updateImage(img);
}

Remember: The function this.updateImage is provided in the original code.

Im the case of an asynchronous function like an upload, a fetch of data or other this approach does NOT work. This is caused by the implementation of JS and fact, that the operation inside the for(Each) loop is not possible to await.

The solution comes with the asyncForEach. The asyncForEach function executes the asynchronous task (uploading the image) and waits for it to be done.

The implementation works like this (details below):

  1. Add the function asyncForEach to your code
  2. call the function asyncForEach when uploading the array

Implementation
To implement the asynchromous upload do the following:
add the following function somewhere in the upper part of your .js file.

// implementation of asynchonous forEach   
// array: array of items to process
// callback: the asynchonous function to process the items 
async asyncForEach(array, callback) {
    for (let index = 0; index < array.length; index++) {
        await callback(array[index], index, array);
    }
}

to then call the newly implemented function call it with the following lines of code
(Note: the variable 'imgArr' represents the array of images to upload):

// create a function 'uploadArray' in which the asyncForEach is called. 
const uploadArray= async () => {
    await this.asyncForEach(imgArr, async (img) => {
        await this.updateImage(img);
    };
/* execute the function. The code following this function call will be 
stalled until the images are either uploaded or failed with error 
(implement error handling in the this.updateImage function
await uploadArray()

Ressources

Upvotes: 0

Renaud Tarnec
Renaud Tarnec

Reputation: 83058

You can use Promise.all(). As explained in the doc:

The Promise.all() method returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled

.....

The returned promise is fulfilled with an array containing all the values of the iterable passed as argument (also non-promise values).

Since your updateImage() function is an async one, you can pass to Promise.all() an array of "calls" to this function (the "iterable" mentionned above) that you generate based on imageArray which is an array of image Objects (the exact same image object you pass to the updateImage() function).

So you could do as follows:

const updateImageArray = async (imageArray) => {
  return Promise.all(imageArray.map(item => updateImage(item)));
}

const imageArray = [....];

updateImageArray(imageArray).then(urls => {
    urls.forEach(element => {
        console.log(element);
    });
})

Or in an async function:

const urlsArray = await updateImageArray(imageArray);

Upvotes: 1

Related Questions