iOS中URLSession downloadTask大文件下载的暂停恢复及resumeData持久化问题
嘿,这个问题我太有共鸣了——之前做视频下载功能时,碰到过几个GB的大文件,resumeData直接飙到几百MB,存内存里直接崩,存磁盘也占空间。下面分享几个我实战过的靠谱方案,帮你搞定大容量resumeData的持久化:
1. 基础方案:直接写入磁盘,但选对存储方式
resumeData本质就是二进制数据,直接写磁盘是最省心的,但要注意几个细节,避免踩坑:
- 选对存储目录:别往
Documents目录写(会被iCloud同步拖慢甚至触发审核问题),优先用Caches目录,或者专门的下载临时目录:let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! - 命名要唯一:用下载URL的哈希值(比如SHA256)作为文件名,避免不同下载任务的resumeData互相覆盖:
func saveResumeData(_ data: Data, for downloadURL: URL) throws { let fileName = downloadURL.absoluteString.sha256() // 自己实现一个SHA256哈希方法 let resumeFileURL = cacheDir.appendingPathComponent("\(fileName).resume") // 用.atomic选项保证写入完整性,避免中途崩溃导致文件损坏 try data.write(to: resumeFileURL, options: .atomic) } - 定期清理:给resumeData加个过期时间,比如7天,App启动时遍历缓存目录,删掉过期的文件,避免磁盘空间被占满。
2. 进阶方案:拆解resumeData,只存核心信息
resumeData其实是个plist格式的二进制包,里面包含了很多冗余信息(比如重复的请求头、无关的会话信息)。我们可以解析它,只提取恢复下载必需的字段,这样能大幅缩小存储体积:
- 先解析resumeData成字典,过滤出关键字段:
func extractResumeInfo(from resumeData: Data) -> [String: Any]? { guard let resumeDict = try? PropertyListSerialization.propertyList(from: resumeData, options: [], format: nil) as? [String: Any] else { return nil } // 只保留恢复下载必需的字段 let criticalKeys = [ NSURLSessionResumeInfoByteRange, // 已下载的字节范围(比如bytes=0-12345) NSURLSessionResumeInfoLocalPath, // 本地临时文件路径 NSURLSessionResumeInfoRemoteURL, // 远程文件URL NSURLSessionResumeInfoEtag, // 服务器文件的ETag(验证文件是否更新) NSURLSessionResumeInfoTimestamp // 暂停时间戳 ] var filteredInfo = [String: Any]() criticalKeys.forEach { key in if let value = resumeDict[key] { filteredInfo[key] = value } } return filteredInfo } - 把过滤后的字典转成JSON或plist写入磁盘,体积能缩小80%以上。恢复下载时,你甚至不需要重新组装resumeData:
- 从保存的信息里拿到已下载的字节范围,生成
Range请求头(比如bytes=12346-) - 创建新的下载请求,设置这个
Range头 - 指定下载到之前的临时文件路径(注意要先检查文件是否存在,系统可能清理临时目录)
- 直接用
downloadTask(with:request)启动下载,就能从断点处继续
- 从保存的信息里拿到已下载的字节范围,生成
3. 避坑提醒
- 服务器支持断点续传是前提:如果服务器没有返回
Accept-Ranges: bytes响应头,哪怕你存了resumeData也恢复不了。所以在开始下载前,最好先发送一个HEAD请求验证服务器是否支持。 - resumeData格式是私有:苹果没有公开resumeData的完整格式,不同iOS版本可能有变化。所以拆解方案要做降级处理——如果解析失败,就 fallback 到直接存储整个resumeData的方式。
- 临时文件迁移:resumeData里的本地路径是系统临时目录,系统可能会自动清理。如果要长期保存,最好把临时文件复制到自己的目录下,同时更新resumeData里的路径信息。
内容的提问来源于stack exchange,提问作者Nikhil Gupta
相关产品推荐
相关产品推荐

