Firebase-ios-sdk: Upload file failed: Error Domain=NSPOSIXErrorDomain Code=100 "Protocol error"

Created on 2 Jun 2020  路  12Comments  路  Source: firebase/firebase-ios-sdk

[REQUIRED] Step 1: Describe your environment

[REQUIRED] Step 2: Describe the problem

Steps to reproduce:

My app let user picks a file from UIDocumentPickerViewController and upload to Firebase storage. However:

  • If I picked images, .txt or .json files, they can be uploaded successfully to Firebase Storage
  • If I selected a custom file like Home Improvement.numbers file from iCloud Drive/Numbers folder, the upload failed with below error. This happened with other extensions like .noto.

Task .<1> finished with error [100] Error Domain=NSPOSIXErrorDomain Code=100 "Protocol error" UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask .<1>, _kCFStreamErrorDomainKey=1, NSErrorPeerAddressKey={length = 28, bytes = 0x1c1e01bb 00000000 24046800 40050801 ... 0000200a 00000000 }, _kCFStreamErrorCodeKey=100, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundUploadTask .<1>",
"LocalUploadTask .<1>"
)}

Relevant Code:

Pick file from UIDocumentPickerViewController:

    func pickFile() {
        let filePicker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .import)
        filePicker.allowsMultipleSelection = false
        filePicker.delegate = self
        present(filePicker, animated: true)
    }

Upload file to Firebase storage

    static func uploadFile(file: FileMeta) -> StorageUploadTask? {
        // Check if user is authenticated
        guard let currentUser = Auth.auth().currentUser else {
            return nil
        }
        // Check if the file exists
        let fileUrl = getLocalFileUrl(fileId: file.id)
        if !FileManager.default.fileExists(atPath: fileUrl.path) {
            return nil
        }
        // Check if existing file is being uploaded
        if let uploadTask = uploadTasks[file.id] {
            return uploadTask
        }
        // Upload file
        let imageRef = Storage.storage().reference(withPath: "users/\(currentUser.uid)/\(file.id)")
        let uploadTask = imageRef.putFile(from: fileUrl, metadata: nil) { (_, error) in
            // Mark current task had finished
            uploadTasks.removeValue(forKey: file.id)
            if error == nil {
                // Keep track of downloaded URL
                imageRef.downloadURL(completion: { (url, err) in
                    if err != nil {
                        print("downloadURL error", err!)
                        return
                    }
                    print("downloadURL", url!.absoluteString)
                    file.downloadURL = url!.absoluteString
                    try? DataStore.shared.saveFile(file)
                })
            }
        }

        // Keep track of running tasks
        uploadTasks[file.id] = uploadTask
        return uploadTask
    }
storage

All 12 comments

@thomasdao Thank you for reporting the issue.

According to the docs UIDocumentPickerViewController in import mode provides URLs to temporary files that may become unavailable to your application. The error may occur when the file may become unavailable during the upload process for some reason. Could you please check if the file by fileUrl is still accessible after the failure occurs. If it is not, please consider copying the file to a directory within you application sandbox before uploading to make sure its accessibility until the end of the process.

Please let us know the results.

Hi @maksymmalyhin I called try fileManager.moveItem(at: tempURL, to: fileURL) to move the temporary file into a folder in documentDirectory. As I mentioned, the upload works fine with image/json/text file, but other files like .numbers or .noto files always failed. I also checked the file in the internal folder, it exists when the file is uploaded

@maksymmalyhin I can reproduce this issue with a small sample code at https://github.com/thomasdao/TestFirebaseUpload

This sample code only upload the files included in the Bundle so we don't have to worry about UIDocumentPickerViewController or the files do not exist.

To run this sample code, first supply your own GoogleService-Info.plist file in TestUpload folder. Since this sample do not use authentication, please modify the Firebase storage security rules to allow public upload. As you can see in the sample, upload image is successful but .numbers file is failed

Hi @thomasdao ,

Thank you for the sample app! One important thing to note (that I just found out myself!) is .numbers files are actually directories. Try printing fileURL.path here and notice the trailing / at the end of the filePath. I believe this is also true for other Apple files like pages. Confusing indeed!

With this in mind, the error likely comes from trying to upload a directory when the putFile method expects a file. Regardless, the error isn't very clear here so we will work on making it fail gracefully with a clearer message when the file URL provided links to a directory. I will attempt to create and provide a work around for now- one idea would be something like zipping the .numbers directory and attempting to upload that.

I hope this was helpful. We will keep you updated!

Thanks @ncooke3! I hope putFile can somehow detect these type of files and upload it transparently, that would be really convenient. Thanks and look forward to the fix :)

Hi @thomasdao, I'm working on adding some better error messages for this type of issue. In terms of a solution, I would consider looking at zipping a file and uploading that to storage. Currently, Firebase doesn't handle this kind of zipping functionality and uploading directories is not supported. I agree it would be a convenient feature and will bring it up with the team when discussing future product features.

@ncooke @thomasdao If I read this right, the issue was resolved and delivered in Firebase 6.27.0. Let me know if that's not the case and we can reopen.

Hi @paulb777, the fix in 6.27.0 is about providing better error message. I was hoping that putFile can handle upload all types of files, that would be a lot more convenient, instead of the client needs to check for the file, zip it when upload and unzip it when download, and do it in all platforms, which can take a lot of work. Thank you :)

Hi @thomasdao - Ah that makes sense. Would you open another feature request with that description to help us track and manage it separate from the confusing message issue that was addressed here? It seems like the issue here was the inability to upload directories. Is that the capability you'd like to see added?

Hi @paulb777 I am only interested in uploading file, but surprisingly file like .numbers is not a single file but a packaged directory. So if Firebase can support directories, it would be great. Otherwise since I'm only interested in uploading file, it's also possible to zip the directory when upload and unzip it when download.

Would you prefer open another feature request or just change the title? I can do both. Thanks!

@thomasdao Thanks for clarifying. My preference is opening another feature request now that we've more clearly defined the request.

@paulb777 I created a new feature request at https://github.com/firebase/firebase-ios-sdk/issues/5869. Thank you.

Was this page helpful?
0 / 5 - 0 ratings