2
votes

i am working on a cooking android app where firebase is the backend, i need to upload multiple images of a recipe in firebase stoarge and then store the downloadurl into firebase database.

i managed to upload the files into firebase but i am having some trouble to get the downloadUrl of these files. i have created an array of promises to upload the files and i had created another array to store the url of each file which i get when it finishes the uploading task.

here is my code

 var promises = [];
 for (var i=0 ;i< prepaImages.length;i++) 
            {
                //alert(prepaImages[i].name);

              var storageRef = firebase.storage().ref("receipes"+"/"+category+"/"+title+"/"+uploadTime+prepaImages[i].name );
             var uploadTask = storageRef.put(prepaImages[i]);
              promises.push(uploadTask);

              uploadTask.on('state_changed', snapshot => {
              var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
                $("#prepaImageUploadprogress").html(Math.round(percentage)+"%");
                $("#prepaImageUploadprogress").attr("style", "width:"+percentage+"%");


                }, error => { alert(error) }, () => {
                    uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
                        //prepaImagesUrl+="+"+downloadURL;
                       prepaImagesUrl.push(downloadURL);
                       });
}); 

the problem is i am getting an array of the length of the number of uploaded files minus one (the legnth it should be equal to the number of uploaded files) and it has the same value (the same downloadurl) . any help will be appreciated Thank you.

1

1 Answers

2
votes

I think the problem is with the promisies. I suggest you to use Promise.all and await. Therefore your code will be more reliable. Here is my solution to multiple file upload (adapt to your variable names):

const array = Array.from({ length: prepaImages.length }, (value, index) => index);
const uploadedImages = await Promise.all(array.map(async index => {
  const image = prepaImages[index];
  const metadata = { contentType: image.type };
  const storageRef = firebase.storage().ref(`receipes/${category}/${title}/${uploadTime}${prepaImages[i].name}`);
  const uploadTask = storageRef.put(image, metadata);
  const url = await new Promise((resolve, reject) => {
    uploadTask.on('state_changed', snapshot => {
      const percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
      $('#prepaImageUploadprogress').html(`${Math.round(percentage)}%`);
      $('#prepaImageUploadprogress').attr('style', `width: ${percentage}%`);
    }, error => reject(error),
    async () => {
      const downloadUrl = await uploadTask.snapshot.ref.getDownloadURL();
      resolve(downloadUrl);
    });
  });
  return { name: image.name, url };
}));

The uploadedImages will contains an array with the image names and download urls. You can make this without await of course, but I prefer this way.

UPDATE: Here is my own code (without error handling) to achieve this, also, I need to mention that I'm using this with react, redux and using the firebase, firestore wrapper for redux redux-firestore and react-redux-firebase but these are just wrappers:

export const addNewWork = work => async (dispatch, getState, { getFirebase, getFirestore  }) => {
  const { files, ...restWork } = work;
  const firebase = getFirebase();
  const firestore = getFirestore();
  const storageRef = firebase.storage().ref();
  const array = Array.from({ length: files.length }, (value, index) => index);
  const uploadedFiles = await Promise.all(array.map(async index => {
    const file = files[index];
    const metadata = { contentType: file.type };
    const uploadTask = storageRef.child(`works/${file.name}`).put(file, metadata);
    const url = await new Promise((resolve, reject) => {
    uploadTask.on('state_changed', () => {}, error => reject(error), async () => {
      const downloadUrl = await uploadTask.snapshot.ref.getDownloadURL();
        resolve(downloadUrl);
      });
    });
    return { name: file.name, url };
  }));

  await firestore.collection('works').add({
    ...restWork,
    image: uploadedFiles[0], // Use only one image for the clean example
    createdAt: new Date()
  });
});