warm__tape
warm__tape

Reputation: 80

Problem retrieving download url of Firebase storage image

I'm trying to retrieve the download URL of a file stored in my firebase storage. I'm storing some data along with each file, hence the firestore "files" collection. Each doc in the files collection is one file in the storage:

const [loading, setLoading] = useState(true);
const [itemData, setItemData] = useState(['']);

const dataRef = firebase.firestore().collection("files");
const fileRef = firebase.storage().ref();

// Load initial data
useEffect(() => {
    return dataRef.get().then(querySnapshot => {

        const list = [];
        querySnapshot.forEach(doc => {

            const { name, uri } = doc.data();

            let imageRef = fileRef.child('/' + uri);

            const imageUrl = imageRef
            .getDownloadURL()
            .then((url) => {
                return url;
            })
            .catch((e) => console.log('Error getting image download URL: ', e));

            console.log(imageUrl);

            list.push({
                id: doc.id,
                name,
                imageUrl: imageUrl
            });

        });

        setItemData(list);

        if (loading) {
            setLoading(false);
        }

    })
    .catch(function(error) {
        console.log("Error getting documents: ", error);
        alert("Error: no document found."); // It doesnt goes here if collection is empty
    })
}, []);

Problem is, that console.log(imageUrl); returns a strange object:

enter image description here

But my itemData contains a correct download URL, but nested deeper in some object thing?

enter image description here

Not sure what's going on here. I just need the url so I can display the image.

Upvotes: 0

Views: 401

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83181

This is because you are not correctly managing the parallel calls to the asynchronous getDownloadURL() method. You should wait the promises returned by the getDownloadURL() method calls are fulfilled before pushing their result to the list array. You should use Promise.all() for that.

The following should do the trick (untested!):

  useEffect(() => {
    const initialArray = [];
    return dataRef
      .get()
      .then((querySnapshot) => {
        const promises = [];

        querySnapshot.forEach((doc) => {
          const { name, uri } = doc.data();
          let imageRef = fileRef.child('/' + uri);
          promises.push(imageRef.getDownloadURL());
          initialArray.push({
            id: doc.id,
            name,
          });
        });
        return Promise.all(promises);
      })
      .then((urlsArray) => {
        // urlsArray has the same number of elements than initialArray

        const fullArray = [];
        urlsArray.forEach((url, index) => {
          const initialObj = initialArray[index];
          const finalObj = Object.assign(initialObj, url);
          fullArray.push(finalObj);
        });

        setItemData(fullArray);

        if (loading) {
          setLoading(false);
        }
      })
      .catch(function (error) {
        console.log('Error getting documents: ', error);
        alert('Error: no document found.'); // It doesnt goes here if collection is empty
      });
  }, []);

Upvotes: 3

Related Questions