You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过await URLSession.shared.download(resumeFrom: data)获取断点续传进度?

如何通过URLSession.shared.download(resumeFrom:)获取断点续传的下载进度

断点续传的进度跟踪核心是结合已下载字节数和本次下载的实时进度来计算整体进度,以下是具体实现步骤:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 10:31:19