如何通过await URLSession.shared.download(resumeFrom: data)获取断点续传进度?
断点续传的进度跟踪核心是结合已下载字节数和本次下载的实时进度来计算整体进度,以下是具体实现步骤:
1. 从续传数据中提取已下载字节数
resumeData中包含了之前中断时的下载状态,我们可以解析它获取已经下载的字节数:
private func getExistingDownloadedBytes(from resumeData: Data) -> Int64? { guard let resumeDict = try? PropertyListSerialization.propertyList(from: resumeData, options: [], format: nil) as? [String: Any], let bytesReceived = resumeDict["NSURLSessionResumeBytesReceived"] as? Int64 else { return nil } return bytesReceived }
2. 基于代理实现带进度跟踪的续传任务
直接使用URLSession.shared.download(resumeFrom:)的async/await方法无法获取进度,需要通过URLSessionDownloadDelegate的代理方法来监听下载过程,结合已下载字节数计算整体进度:
自定义下载管理器示例
class ResumeDownloadManager: NSObject, URLSessionDownloadDelegate { private var existingBytes: Int64 = 0 private var progressCallback: ((Double) -> Void)? private var completionCallback: ((Result<URL, Error>) -> Void)? func startResumeDownload(with resumeData: Data, progressHandler: @escaping (Double) -> Void, completionHandler: @escaping (Result<URL, Error>) -> Void) { self.existingBytes = getExistingDownloadedBytes(from: resumeData) ?? 0 self.progressCallback = progressHandler self.completionCallback = completionHandler let session = URLSession(configuration: .default, delegate: self, delegateQueue: .main) let task = session.downloadTask(withResumeData: resumeData) task.resume() } // 解析已下载字节数 private func getExistingDownloadedBytes(from resumeData: Data) -> Int64? { guard let resumeDict = try? PropertyListSerialization.propertyList(from: resumeData, options: [], format: nil) as? [String: Any], let bytesReceived = resumeDict["NSURLSessionResumeBytesReceived"] as? Int64 else { return nil } return bytesReceived } // 实时回调进度 func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { let totalDownloaded = existingBytes + totalBytesWritten let totalFileSize = existingBytes + totalBytesExpectedToWrite guard totalFileSize > 0 else { return } let progress = Double(totalDownloaded) / Double(totalFileSize) progressCallback?(progress) } // 下载完成回调 func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { completionCallback?(.success(location)) } // 下载错误回调 func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let error = error { completionCallback?(.failure(error)) } } }
使用方式
let downloadManager = ResumeDownloadManager() downloadManager.startResumeDownload(with: yourResumeData) { progress in print("当前下载进度:\(String(format: "%.1f%%", progress * 100))") } completionHandler: { result in switch result { case .success(let fileURL): print("续传完成,文件路径:\(fileURL)") case .failure(let error): print("续传失败:\(error.localizedDescription)") } }
3. 注意事项
- 如果
totalBytesExpectedToWrite返回-1(NSURLSessionTransferSizeUnknown),说明无法获取总文件大小,此时只能展示已下载字节数,无法计算百分比进度。 - 解析
resumeData的键名NSURLSessionResumeBytesReceived是系统标准字段,当前iOS版本通用,无需担心兼容性问题。 - 注意
URLSession的生命周期管理,避免出现内存泄漏。
内容的提问来源于stack exchange,提问作者Adam Mischke vanities
相关产品推荐
相关产品推荐

