Search code examples
firebasereact-nativegoogle-cloud-platformgoogle-cloud-storagefirebase-storage

How can I return the download URL of an image uploaded to firebase storage


I want to upload an image to firebase (which is working), then return the download URL of the image and store it as a string.

Here is my code:

uploadImage = async (uri, imageName) => {
    const response = await fetch(uri);
    const blob = await response.blob();

    firebase.storage().ref().child(imageName).put(blob)
      .then(snap => {
        return snap.ref.getDownloadURL();
      })
      .then(downloadURL => {
        return downloadURL;
      })
      .catch(error => {
        console.log(`An error occurred while uploading the file.\n\n${error}`);
      });
  }

The image uploads to firebase storage just fine. At the moment it just shows this when I try write the URL of the uploaded image to the database: https://ibb.co/WHHHxBY

Here is the block of code where I create the user record:

  firebase
    .auth()
    .createUserWithEmailAndPassword(this.state.email, this.state.password)
    .then(userCredentials => {
      let imageUrl = '';

      let db = firebase.database().ref('users/' + userCredentials.user.uid);

      if (this.state.image) {
        imageUrl = this.uploadImage(this.state.image.uri, `images/user-${userCredentials.user.uid}`);
      }

      db.set({
        email: this.state.email,
        imageUrl: imageUrl,
        username: this.state.username
      });

      return userCredentials.user.updateProfile({
        displayName: this.state.username
      });
    })
    .catch(error => this.setState({ errorMessage: error.message }));

Solution

  • In your uploadImage function, you are chaining the promises but you don't return the chain. You should adapt it as follows:

    uploadImage = async (uri, imageName) => {
        const response = await fetch(uri);
        const blob = await response.blob();
    
        return firebase.storage().ref().child(imageName).put(blob)  // <-- Here return the chain
          .then(snap => {
            return snap.ref.getDownloadURL();
          })
          .then(downloadURL => {
            return downloadURL;
          })
          .catch(error => {
            console.log(`An error occurred while uploading the file.\n\n${error}`);
          });
      }
    

    However, you could transform this code in async/await "style", as follows:

    uploadImage = async (uri, imageName) => {
    
        try {
            const response = await fetch(uri);
            const blob = await response.blob();
    
            const snap = await firebase.storage().ref().child(imageName).put(blob);
    
            const downloadURL = await snap.ref.getDownloadURL();
    
            return downloadURL;
    
        } catch (e) {
            console.error(e);
            throw e;
        }
    
    }
    

    Then, since this uploadImage function is asynchronous you should adapt the way you call it. I suggest to modify the other part of your code as follows:

    try {
         const userCredentials = await firebase
            .auth()
            .createUserWithEmailAndPassword(this.state.email, this.state.password);
    
         let imageUrl = '';
    
         const db = firebase.database().ref('users/' + userCredentials.user.uid);
    
         if (this.state.image) {
    
            imageUrl = await this.uploadImage(this.state.image.uri, `images/user-${userCredentials.user.uid}`);
    
            await db.set({
                email: this.state.email,
                imageUrl: imageUrl,
                username: this.state.username
              });
    
    
              return userCredentials.user.updateProfile({
                displayName: this.state.username
              });
    
         }
         //You should probably manage the else case
    
    } catch (e) {
         this.setState({ errorMessage: e.message })
    }