1
votes

Language : Swift 5

iOS: 13.2

macOS: Catalina 10.15.4

Firebase Storage Rules:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth!=null;
    }
  }
}

The code to upload image and save download URL: (Which works fine, because I can see images uploaded to storage and their respective download URLs stored to real-time database.)

        let storageRef = Storage.storage().reference()

        //Let's upload all workout pictures
        let uploadPicsRef = 
        storageRef.child("WORKOUTDATA/USERS/"+self.UID!).child("WHITEBOARDWORKOUTS")

        let uploadNumberRef = uploadPicsRef.child("\(String(describing: workoutNum))")
        let workoutPicturesRef = uploadNumberRef.child("WORKOUTPICTURES")
        let workoutPicURLRef = workoutRef.child("WORKOUTPICTURESURL")
        var count = 0
        var picNumber = 0

        //workoutPictures list/array contains images selected from iPhone Gallery, using 
        //UIImagePickerController
        for workoutPic in self.workoutPictures
        {
            let workoutPicData = workoutPic.jpegData(compressionQuality: 1.0)!
            count = count + 1
            let pictureName = "Picture\(count).jpg"

            // Upload the file to the path in pictureRef
            let pictureRef = workoutPicturesRef.child("\(pictureName)")

            let metaData = StorageMetadata()
            metaData.contentType = "image/jpg"
            pictureRef.putData(workoutPicData, metadata: metaData) { (metadata, error) in
                if error != nil {
                    print("Error while uploading image")
                }
                else
                {
                    pictureRef.downloadURL { (url, err) in
                        picNumber = picNumber + 1
                        workoutPicURLRef.child("Picture\(picNumber)").setValue(url?.absoluteString)
                    }
                }
            }
        }

The code to download image:

        let myGroup = DispatchGroup()

        let workoutPicUrls = snapshot.childSnapshot(forPath: "WORKOUTPICTURESURL")

        for url in workoutPicUrls.children
        {
            myGroup.enter()
            let snap = url as! DataSnapshot
            let link = snap.value as? String
            let storageRef = Storage.storage().reference()
            let pictureRef = storageRef.root().child(link!)

            DispatchQueue.main.async {
                pictureRef.getData(maxSize: 1*2000000*2000000) { (data, err) in
                if (err != nil) {
                    print(err!)
                    print(err!.localizedDescription)

                } else {
                let pic = UIImage(data: data!)
                workoutPicsArray.append(pic!)
                myGroup.leave()
                }
            }
            }
        }

Error:

Error Domain=FIRStorageErrorDomain Code=-13010 "Object https:/firebasestorage.googleapis.com/v0/b/trainer-8cb52.appspot.com/o/WORKOUTDATA%2FUSERS%2F1K7WV1alYIeWPAsFC6YMoJKPFSj1%2FWHITEBOARDWORKOUTS%2F5%2FWORKOUTPICTURES%2FPicture1.jpg?alt=media&token=785ab8c7-1e08-4ad3-a542-c9e6313eb547 does not exist." UserInfo={object=https:/firebasestorage.googleapis.com/v0/b/trainer-8cb52.appspot.com/o/WORKOUTDATA%2FUSERS%2F1K7WV1alYIeWPAsFC6YMoJKPFSj1%2FWHITEBOARDWORKOUTS%2F5%2FWORKOUTPICTURES%2FPicture1.jpg?alt=media&token=785ab8c7-1e08-4ad3-a542-c9e6313eb547, ResponseBody={ "error": { "code": 404, "message": "Not Found. Could not get object", "status": "GET_OBJECT" } }, bucket=trainer-8cb52.appspot.com, data={length = 115, bytes = 0x7b0a2020 22657272 6f72223a 207b0a20 ... 54220a20 207d0a7d }, data_content_type=application/json; charset=UTF-8, NSLocalizedDescription=Object https:/firebasestorage.googleapis.com/v0/b/trainer-8cb52.appspot.com/o/WORKOUTDATA%2FUSERS%2F1K7WV1alYIeWPAsFC6YMoJKPFSj1%2FWHITEBOARDWORKOUTS%2F5%2FWORKOUTPICTURES%2FPicture1.jpg?alt=media&token=785ab8c7-1e08-4ad3-a542-c9e6313eb547 does not exist., ResponseErrorDomain=com.google.HTTPStatus, ResponseErrorCode=404}

What I have tried so far:

  1. Checked firebase storage rules.
  2. When I paste the path https:/firebasestorage.googleapis.com/v0/b/trainer8cb52.appspot.com/o/WORKOUTDATA%2FUSERS%2F1K7WV1alYIeWPAsFC6YMoJKPFSj1%2FWHITEBOARDWORKOUTS%2F5%2FWORKOUTPICTURES%2FPicture1.jpg?alt=media&token=785ab8c7-1e08-4ad3-a542-c9e6313eb547 in chrome browser window, the expected image opens.
  3. Set the maxSize to a ridiculously high number 1*2000000*2000000.

Thank you!

1
Download URLs are not affected by security rules at all. The error message is saying the image is not found for the URL you're using. Note the text "does not exist" in the error.Doug Stevenson
@DougStevenson When I paste the URL https:/firebasestorage.googleapis.com/v0/b/trainer8cb52.appspot.com/o/WORKOUTDATA%2FUSERS%2F1K7WV1alYIeWPAsFC6YMoJKPFSj1%2FWHITEBOARDWORKOUTS%2F5%2FWORKOUTPICTURES%2FPicture1.jpg?alt=media&token=785ab8c7-1e08-4ad3-a542-c9e6313eb547 in chrome browser window, the expected image opens. It wouldn't have worked if the image didn't exist at the URL, right? Please correct me if I'm thinking wrong here.Siddhika Ghaisas

1 Answers

0
votes

Is it possible that you are storing the full https URL in the database and are trying to create a reference by adding the full https url as a child to the storage reference?

I think you should try to either store just the path and name in your database or you change your download code to use the https URL.

       // Create a reference from an HTTPS URL
       // Note that in the URL, characters are URL escaped!
       let httpsReference = storage.reference(forURL: "https://firebasestorage.googleapis.com/b/bucket/o/images%20stars.jpg")

       httpsReference.getData(maxSize: ...

Also you're running your getData method inside DispatchQueue.main.async. getData has itself a completion handler and might take some time, when you run that inside of DispatchQueue.main.async it will block your code until the download is done. Only put code that update the UI inside DispatchQueue.main.async. In your case as soon as you do something with your workoutPicsArray or the UIImage to update your view.

Have a look here to see if you can figure out how you are actually trying to get the data. It might be helpful to put a print() after each line to see what you are creating and using at what point.

Download Files on iOS