Jannemannen
Jannemannen

Reputation: 125

How to upload more than one picture to Firebase and download the URL

I'm trying to upload a set of images to Firebase. The upload itself works, the pictures get added. However, I'd like to get an URL of the pictures.

I figured that I could store them in an Array, but whatever I do, only one picture gets stored in the URL.

I want to A, upload a set of images (max 3) or B, upload only one image. Therefore, I set the target files in a state, and use that state to check if there are one or more images. Then I loop through the upload for x amount of times, and I push the response into ...URL. No matter what I do, I end up with only one image URL in firebase.

Going to try to clarify a bit more: I want to upload up to three images to Firebase (this works, the images gets uploaded into the image-bucket.

What I want, is to get the download URL of the images (I want to use the image URL in another component. Essentially, I want to create an image gallery or an image carousel with the images.

Basically, fetching the image URL from Firebase and display the images.

This is what is displayed in firebase: https://i.sstatic.net/BKkym.jpg

This is a console.log(url): https://i.sstatic.net/2VvL4.jpg

 const AddCountryForm = () => {

  const [url, setUrl] = useState([]);
  const [fileArray, setFileArray] = useState([]);



  function onSubmit(e) {
    firebase
      .firestore()
      .collection(continent)
      .doc(revName)
      .set({
        url,

      })
      .then(() => {
        setUrl("");

      });
  }


  const handleChange = (e) => {
    setFileArray(e.target.files);
  };


  const handleUpload = (e) => {
    e.preventDefault();


    if (fileArray.length > 1) {
      for (let i = 0; i < fileArray.length; i++) {
        const uploadTask = storage.ref(`images/${fileArray[i].name}`).put(fileArray[i]);

        uploadTask.on(
          "state_changed",

          () => {
            storage
              .ref("images")
              .child(fileArray[i].name)
              .getDownloadURL()
              .then(res => {
                setUrl([...url, res]); 
              });
          }

        );
      }
    }
    if (fileArray.length === 1) {
      const uploadTask = storage.ref(`images/${fileArray[0].name}`).put(fileArray[0]);
      uploadTask.on(
        "state_changed",

        () => {
          storage
            .ref("images")
            .child(fileArray[0].name)
            .getDownloadURL()
            .then(res => {
              setUrl([res]);
            });
        }
      );
    }

  };

return (
    <form onSubmit={handleSubmit(onSubmit)}>
          <div>
        <input type="file" onChange={handleChange} required multiple />
        <button onClick={handleUpload}> Upload</button>
      </div>

      <input type="submit"></input>
    </form>
  );
};
export default AddCountryForm;

Upvotes: 0

Views: 121

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83191

You want to execute several put() and getDownloadURL() asynchronous calls to Firebase Storage in parallel.

You have to use Promise.all(), since put() returns an UploadTask (which "behaves like a Promise, and resolves with its snapshot data when the upload completes") and getDownloadURL() returns a Promise.

Therefore, the following should do the trick:

  if (fileArray.length > 1) {
    const uploadPromises = [];
    for (let i = 0; i < fileArray.length; i++) {
      uploadPromises.push(
        storage.ref(`images/${fileArray[i].name}`).put(fileArray[i])
      );
    }
    Promise.all(uploadPromises)
      .then((uploadTaskSnapshots) => {
        const urlPromises = [];

        uploadTaskSnapshots.forEach((uploadTaskSnapshot) => {
          urlPromises.push(uploadTaskSnapshot.ref.getDownloadURL());
        });

        return Promise.all(urlPromises);
      })
      .then((urls) => {
        setUrl(urls);
      });
  }

Upvotes: 2

Related Questions