Swift对接Firebase Storage上传多类型文件报错排查
问题原因
第二次上传触发POSIX 40错误、上传失败,是代码里三个明显问题导致的,和Firebase服务端无关:
- 选中文件后没有手动关闭文档选择器。UIDocumentPickerViewController不会在用户选完文件后自动dismiss,第一次选完后选择器一直残留在视图栈中,第二次触发上传时会出现视图层级冲突,连带破坏文件访问的权限链路。
- 直接使用系统返回的临时URL上传。
.import模式下系统返回的URL指向App临时tmp目录,系统会不定期自动清理该目录下的文件,且该URL的访问权限只在pick回调的当前生命周期内有效,第一次上传时权限和文件都存在所以能成功,后续再访问就会因为权限失效/文件被清理报错。 - 没有对上传任务做强引用。
putFile生成的上传任务如果没有被属性持有,会在回调方法执行结束后被ARC自动回收,第一次上传可能因为执行速度快刚好跑完,第二次如果遇到内存紧张、文件稍大,任务中途被回收就会直接报网络/链路错误。 - 额外逻辑漏洞:你开了多选支持但永远只取
urls.first,没有做空值兜底,用户选多个文件时很容易拿到无效路径。
修复方案
直接替换对应代码即可:
- 先在类里新增两个必要属性
// 持有当前上传任务,避免被系统回收 var currentUploadTask: StorageUploadTask? // 自定义上传临时目录路径,自己管理文件避免被系统清理 let uploadTempDir: URL = { let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! let tempDir = cacheDir.appendingPathComponent("CustomUploadTemp", isDirectory: true) if !FileManager.default.fileExists(atPath: tempDir.path) { try? FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) } return tempDir }()
- 替换原有的
didPickDocumentsAt代理实现
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { // 先关闭选择器,避免视图栈残留 controller.dismiss(animated: true) guard let selectedURL = urls.first else { return } let fileManager = FileManager.default // 把系统返回的临时文件拷贝到自己管理的目录,永久持有访问权限 let targetFileURL = uploadTempDir.appendingPathComponent(selectedURL.lastPathComponent) // 存在同名旧文件先删除 if fileManager.fileExists(atPath: targetFileURL.path) { try? fileManager.removeItem(at: targetFileURL) } do { try fileManager.copyItem(at: selectedURL, to: targetFileURL) } catch { print("文件预处理失败:\(error)") return } // 用可控的本地路径上传 let fileRef = storage.reference() .child("Customer Name") .child(selectedURL.deletingPathExtension().lastPathComponent) currentUploadTask = fileRef.putFile(from: targetFileURL, metadata: nil) { _, error in // 上传结束不管成功失败,删除本地临时拷贝释放空间 try? fileManager.removeItem(at: targetFileURL) if let error = error { print("上传失败:\(error)") return } print("上传成功") } }
补充说明
- 要实现多文件上传的话,遍历
urls数组,逐个执行拷贝、上传逻辑即可,不要只取第一个元素。 - 文件名里的空格、括号、中文不需要手动转义,Firebase SDK会自动处理。
- 如果要支持后台上传大体积视频,记得在AppDelegate中实现背景URL会话回调,将系统提供的completionHandler传递给Firebase Storage,避免后台任务被系统挂起。
内容的提问来源于stack exchange,提问作者Mariglen Meta
相关产品推荐
相关产品推荐

