Search code examples
javascriptfirebasegoogle-cloud-platformgoogle-cloud-storagefirebase-storage

'getDownloadURL' returns one less URL in a Promise


I'm trying to upload several pictures to Firebase Storage and get their URLs to add these as a field in a gym's profile. However, with the code I came up with, always one URL is not returned. If I upload 3 pictures, 3 pictures appear in Storage but I only get 2 URLs. How do I fix that?

const promises = [];
const URLarray = [];

imageAsFile.forEach(img => {
            const uploadTask = storage.ref().child(`/photos/${doc.id}/${img.name}`).put(img);
            promises.push(uploadTask);
            uploadTask.then((uploadTaskSnapshot) => {
                return uploadTaskSnapshot.ref.getDownloadURL();
            }).then((pic) => {
                const addPics = db.collection("gyms").doc(doc.id)
                promises.push(addPics);
                addPics.update({gymPhoto: firebase.firestore.FieldValue.arrayUnion(pic)});
                // URLarray.push(pic)
            })
    });

Promise.all(promises).then(() => {
    // console.log(URLarray)
    alert("Done");
})

Solution

  • The following should work (untested):

    const promises = [];
    
    imageAsFile.forEach(img => {
        const uploadTask = storage.ref().child(`/photos/${doc.id}/${img.name}`).put(img);
        promises.push(uploadTask);
    });
    
    Promise.all(promises)
        .then(uploadTaskSnapshotsArray => {
            const promises = [];
            uploadTaskSnapshotsArray.forEach(uploadTaskSnapshot => {
                promises.push(uploadTaskSnapshot.ref.getDownloadURL());
            });
    
            return Promise.all(promises);
        })
        .then(urlsArray => {
            const docRef = db.collection("gyms").doc(doc.id);
            return docRef.update({ gymPhoto: firebase.firestore.FieldValue.arrayUnion(...urlsArray) });
        })
    

    Promise.all() returns a single promise: This promise is fulfilled with an Array containing all the resolved values in the Array passed as the argument.

    So, if you call Promise.all() with the Array of uploadTasks, when the returned promise is fulfilled you will get an Array of uploadTaskSnapshots.

    Then you need to use this Array to get an Array of URLs, again by using Promise.all().

    Then, when you get the Array of URLs, you need to update only once the document with id = doc.id. So here you don't need Promise.all(). What you need to do is to pass several values to the FieldValue.arrayUnion() method. For that you need to use the Spread operator, as follows:

    docRef.update({ gymPhoto: firebase.firestore.FieldValue.arrayUnion(...urlsArray) });