Firebase Storage (web) not working when files are uploaded from phone

Solution for Firebase Storage (web) not working when files are uploaded from phone
is Given Below:

I’m creating a website using Firebase. The user should be able to upload & update their profile picture, so long as it is <10mb, and is a .jpg, .png, or .gif file.

What is supposed to happen:

  1. User uploads a valid image
  2. Create or update a reference /users/{uid}/profileImage within Firebase Storage
  3. Use the getDownloadURL() method to get the image’s URL, and store it as text inside Firestore, under the user’s profile information
  4. Use that URL as the src of the profile picture.

When I try to do this on my computer (Windows 10), it works perfectly. However, when I try to do it on my phone (iPhone 8; iOS 14.7.1), it does not work. The image uploaded from the phone reaches the /users/{uid}/profileImage, but then it doesn’t properly get the downloadURL due to permission problems, even though the user is authenticated on the phone browser, as required by the rules.

The following is the code to (1) get the file and (2) update the user’s profile picture:

// Grab dp img and store it in file var
let file = {}
const chooseFile = (e) => {
    // Get the file from local machine
    file = e.target.files[0]
    console.log(file )
}

// Store dp in storage as file, and db as link
const updateDp = (currentUser) => {
    // Check if new dp has been added/exists.
    if ("name" in file) {
        // Check if uploaded file is an image
        if (file.type !== "image/jpeg" && file.type !== "image/png" && file.type !== "image/gif") {
            alert("You can only upload .jpeg, .jpg, .png and .gif under 10mb")
            return
        }

        // Check image file size
        if (file.size/1024/1024>10) {
            alert("The image size must be under 10mb")
            return
        }

        // Create storage ref & put the file in it
        storage
            .ref("users/" + currentUser.uid + "/profileImage")
            .put(file)
            .then(() => {
                // success => get download link, put it in DB, update dp img src
                storage
                    .ref("users/" + currentUser.uid + "/profileImage")
                    .getDownloadURL()
                    .then(imgURL => {
                        db
                            .collection("users")
                            .doc(currentUser.uid)
                            .set({
                                dp_URL: imgURL,
                                dp_URL_last_modified: file.lastModifiedDate
                            }, {
                                merge: true
                            })
                        document.querySelector("#nav_dp").src = imgURL;
                    })
                console.log("success")
            }).catch(() => {
                console.log(error.message)
            })
    } else {
        console.log("Empty/no file")
    }

}

The following is my Firebase Storage Rules:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{uid}/{profileImage} {
      allow read: if request.auth!=null;
      allow write: if request.auth!=null && request.auth.uid == uid;
    }
  }
}

The security rules and code look fine to me except the console.log("success") will log before you get the downloadURL as getDownloadURL() returns a Promise. Try changing your function to an async function as shown:

// Store dp in storage as file, and db as link
const updateDp = async (currentUser) => {
  //             ^^^^^
  // Check if new dp has been added/exists.
  if ("name" in file) {
    try {
      // Check if uploaded file is an image
      if (
        file.type !== "image/jpeg" &&
        file.type !== "image/png" &&
        file.type !== "image/gif"
      ) {
        alert("You can only upload .jpeg, .jpg, .png and .gif under 10mb");
        return;
      }

      // Check image file size
      if (file.size / 1024 / 1024 > 10) {
        alert("The image size must be under 10mb");
        return;
      }

      // Create storage ref & put the file in it
      const userPicRef = storage.ref(
        "users/" + currentUser.uid + "/profileImage"
      );
      await userPicRef.put(file);
      console.log("Image uploaded")
      // success => get download link, put it in DB, update dp img src
      const imgURL = await UserPicRef.getDownloadURL();
      console.log(`Image URL: ${imgURL}`)
      await db.collection("users").doc(currentUser.uid).set(
        {
          dp_URL: imgURL,
          dp_URL_last_modified: file.lastModifiedDate,
        },
        {
          merge: true,
        }
      );
      console.log("Document Added")
      document.querySelector("#nav_dp").src = imgURL;
    } catch (error) {
      console.log(error);
    }
  } else {
    console.log("Empty/no file");
  }
};

Answering my own question.

Turns out, I was using file.lastModifiedDate, which is a depreciated method. It works on the Google Chrome app ran on Android, but not in iOS.

To fix it, I used file.lastModified instead.