Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Firebase Storage's putFile() method is resulting in: The file “fileName” couldn’t be opened error

Here are two ways I've tried to upload the file:

1.

getURLOfPhoto(assetURL: imagesDictionary[String(whichProfileImage)]! , completionHandler: { (responseURL) in          
                                    FIRStorage.storage().reference().putFile(responseURL as! URL)
                            })

2.

  let assets = PHAsset.fetchAssets(withALAssetURLs: [imagesDictionary[String(whichProfileImage)] as! URL], options: nil)
            let asset = assets.firstObject
            asset?.requestContentEditingInput(with: nil, completionHandler: { (contentEditingInput, info) in
                let imageFile = contentEditingInput?.fullSizeImageURL?

                FIRStorage.storage().reference().child("test").putFile(imageFile!, metadata: nil) { (metadata, error) in
                        if let error = error {                   
                            return
                        }   
                }
            })

I am getting this error:

 Body file is unreachable: /var/mobile/Media/DCIM/100APPLE/picture.JPG        
    Error Domain=NSCocoaErrorDomain Code=257 "The file “picture.JPG” couldn’t be opened because you don’t have permission to view it."     
        UserInfo={NSURL=file:///var/mobile/Media/DCIM/100APPLE/picture.JPG, NSFilePath=/var/mobile/Media/DCIM/100APPLE/picture.JPG,     
    NSUnderlyingError=0x15da49a0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}

The URL seems to be being retrieved successfully and the error only occurs when the putFile() method gets called.

Does anyone know how to fix this error or another way of uploading a file (not a Data object) to Firebase Storage?

Thanks in advance

like image 281
Micah Simmons Avatar asked Jul 17 '16 19:07

Micah Simmons


3 Answers

Currently Firebase Storage is unable to use file URLs that are retrieved using the PHAsset based code I used in my question (or at least it was't able to in my experience) - even if those files are files the user took with the camera on their own iPhone. So, one solution is to re-save the file in question to a location which is accessible to the Firebase Storage API and then upload the file by passing in that location's URL in to the putFile() method.

You can use this method if you're using the imagePickerController() method:

do {
   let documentsURL = FileManager.default().urlsForDirectory(.documentDirectory,
                                                                          inDomains: .userDomainMask)[0]
   let fileURL = try documentsURL.appendingPathComponent("fileName.jpg")

   let image = info[UIImagePickerControllerOriginalImage]

   try UIImageJPEGRepresentation(image as! UIImage,1.0)?.write(to: fileURL, options: [])

                    FIRStorage.storage().reference().child("exampleLocation")
                        .putFile(fileURL, metadata: nil) { (metadata, error) in
    if let error = error {
                                print("Error uploading: \(error.description)")
                                return
                            }
                       }                
                 }
         catch {
            print("error is ", error)
        }
like image 146
Micah Simmons Avatar answered Oct 22 '22 22:10

Micah Simmons


It's possible that our uploader doesn't have the correct permissions to access that file due to the app sandbox (and we're pretty hesitant to grant broad file system access permissions).

I only recommend storing files in Documents/ and tmp/ per https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html

Granted, if it's coming from system resources, we might want to revisit that behavior. Typically I just do (yes, I know it's data instead of file and thus will have worse memory behavior):

  // pragma mark - UIImagePickerDelegate overrides
  func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

    // Get local image
    guard let image: UIImage = info[UIImagePickerControllerOriginalImage] as? UIImage else { return }
    let imageData = UIImagePNGRepresentation(image)!

    // Get a reference to the location where we'll store our photos
    let photosRef = storage.reference().child("chat_photos")

    // Get a reference to store the file at chat_photos/<FILENAME>
    let photoRef = photosRef.child("\(NSUUID().UUIDString).png")

    // Upload file to Firebase Storage
    let metadata = FIRStorageMetadata()
    metadata.contentType = "image/png"
    photoRef.putData(imageData, metadata: metadata).observeStatus(.Success) { (snapshot) in
      // When the image has successfully uploaded, we get it's download URL
      let text = snapshot.metadata?.downloadURL()?.absoluteString
    }

    // Clean up picker
    dismissViewControllerAnimated(true, completion: nil)
  }
like image 32
Mike McDonald Avatar answered Oct 22 '22 22:10

Mike McDonald


@Mike McDonald, thanks for your answer it worked for me. I was having the exact same issue and was able to solve with your suggestions. Here is my code:

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

    guard let image: UIImage = info[UIImagePickerControllerOriginalImage] as! UIImage else { return }
    let profileImageName = "profileImageName.png"
    let imageData = UIImagePNGRepresentation(image)!
    let filePath = "\(FIRAuth.auth()!.currentUser!.uid)/\(Int(NSDate.timeIntervalSinceReferenceDate() * 1000))"

    let photoStorageRef = FIRStorage.storage().reference().child(filePath)
    let photoRef = photoStorageRef.child("\(profileImageName)")

    let metadata = FIRStorageMetadata()
    metadata.contentType = "image/png"

    photoRef.putData(imageData, metadata: metadata) { metadata, error in
        if let error = error {
            print("Error uploading:\(error.localizedDescription)")
            return
        } else {
            guard let downloadURL = metadata!.downloadURL() else { return }
            guard let downloadURLString = metadata!.downloadURL()?.absoluteString else { return }

            //do what I need to do with downloadURL
            //do what I need to do with downloadURLString
        }
    }

Hope this can help anyone else having the same issue!

like image 1
alexisSchreier Avatar answered Oct 22 '22 22:10

alexisSchreier