iOS Swift Firebase Storage后台下载大文件报错求助
Firebase Storage后台下载大文件超时问题解决建议
1. 手动管理后台任务生命周期
系统警告的核心是后台任务未及时调用endBackgroundTask(_:)。GTMSessionFetcher的自动后台任务管理在多任务场景下可能失效,建议手动为每个下载任务创建并维护后台任务:
for urlString in [ ... ] { DispatchQueue.global(qos: .background).async { var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid // 启动后台任务 backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "FirebaseStorageDownload") { // 任务超时回调,手动结束任务 UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } let ref = self.storage.reference(forURL: urlString) ref.write(toFile: fileURL) { _, error in defer { // 无论成功失败,结束后台任务 if backgroundTaskID != .invalid { UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } } if let error = error { print(value: error, type: .error) onDemand(.failure(error)) } else { onDemand(.success(true)) } } } }
2. 用OperationQueue控制并发下载数
全局队列的并发控制不够精准,改用OperationQueue限制同时运行的下载任务数量,避免后台任务堆积:
let downloadQueue = OperationQueue() downloadQueue.maxConcurrentOperationCount = 2 // 根据需求调整,建议2-3个 downloadQueue.qualityOfService = .background for urlString in [ ... ] { let operation = BlockOperation { [weak self] in guard let self = self else { return } var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "FirebaseStorageDownload") { UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } let ref = self.storage.reference(forURL: urlString) ref.write(toFile: fileURL) { _, error in defer { if backgroundTaskID != .invalid { UIApplication.shared.endBackgroundTask(backgroundTaskID) backgroundTaskID = .invalid } } if let error = error { print(value: error, type: .error) onDemand(.failure(error)) } else { onDemand(.success(true)) } } } downloadQueue.addOperation(operation) }
3. 配置GTMSessionFetcher的后台会话
确保Firebase Storage使用的GTMSessionFetcher配置了正确的后台会话,避免默认会话无法在后台持久:
// 初始化Storage时配置后台会话 let config = URLSessionConfiguration.background(withIdentifier: "com.yourapp.firebase.storage.background") config.isDiscretionary = true // 允许系统选择合适的时间执行后台任务 config.sessionSendsLaunchEvents = true // 任务完成时唤醒应用 let storage = Storage.storage(configuration: StorageConfiguration(sessionConfiguration: config))
4. 实现断点续传应对长时下载
大文件下载超过30秒易触发系统警告,建议用Firebase的断点续传API,保存下载进度,在后台任务超时后恢复下载:
// 保存resumeData的示例(可存在UserDefaults或本地文件) var resumeDataMap: [String: Data] = [:] func startDownload(urlString: String, fileURL: URL) { let ref = storage.reference(forURL: urlString) var downloadTask: StorageDownloadTask? if let resumeData = resumeDataMap[urlString] { // 恢复之前的下载 downloadTask = ref.write(toFile: fileURL, resumeData: resumeData) } else { // 新下载任务 downloadTask = ref.write(toFile: fileURL) } downloadTask?.observe(.pause) { snapshot in if let resumeData = snapshot.resumeData { resumeDataMap[urlString] = resumeData // 持久化resumeData } } downloadTask?.observe(.success) { _ in resumeDataMap.removeValue(forKey: urlString) onDemand(.success(true)) } downloadTask?.observe(.failure) { snapshot in if let error = snapshot.error as NSError? { if error.code == StorageErrorCode.pause.rawValue { // 任务被暂停,保存resumeData if let resumeData = snapshot.resumeData { resumeDataMap[urlString] = resumeData } } else { print(value: error, type: .error) onDemand(.failure(error)) } } } }
额外检查项
- 确认
info.plist中Permitted background task scheduler identifiers的值是GTMSessionFetcher-firebasestorage.googleapis.com,且拼写无误。 - 确保应用已开启
Background processing和Background fetch能力(在Xcode的Signing & Capabilities中配置)。 - 避免在后台任务中执行无关的UI操作或高耗时同步任务,保持后台任务专注于下载本身。
内容的提问来源于stack exchange,提问作者Maxime Bianchi
相关产品推荐
相关产品推荐

