设计一个健壮的大型文件下载系统
转至简书作者:http://mp.weixin.qq.com/s?__biz=MzAxMzE2Mjc2Ng==&mid=2652156080&idx=1&sn=1dce429082c2b688036a958f2d6680a9&chksm=8046d0d1b73159c78e820727da584675cd3263b6aebf675118099c4b9470a8a31528f86ed36f&mpshare=1&scene=23&srcid=0420Kvh9aXJHmLwlkbNDiHXo#rd
想要让 NSScreencast iOS 应用支持下载视频供离线使用。每个视频大小为 80-200 MB,这就需要打造一个健壮的下载系统。
第一步是在章节页面添加显示进度的下载按钮。类似于你在 iTunes 下载歌曲。点击下载按钮动画进入圆形下载进度指示器。该下载在后台进行,并向订阅的按钮发送进度通知,以在 UI 中显示百分比进度。
实际的下载是通过 NSOperation 子类完成的。这种方式提供了很大的便利,例如控制并发性、服务优先级,并且拥有便捷的机制取消运行的中的下载。下载进度通过通知发送到外界,通知包含章节 ID,所有感兴趣的 UI 可以监听该通知并做出更新。
当然,我们不需要在下载时让用户盯着屏幕,因此用户可以自由切换应用甚至暂停它,下载都可以继续。
接下来,我需要在统一的位置显示挂起和之前的下载,用于方便查看哪些内容正在下载,哪些下载完成,并可以离线观看和删除视频。
该页面在表视图中以行显示每个章节的数据。各行显示的下载内容需要在收到下载进度变化通知时快速刷新。
为了实现上述功能,我把该状态保存成 Core Data 模型。我的 DownloadInfo 模型如下所示:
有了 Core Data,我们可以跟踪下载的整个生命周期状态。以前,我用过 plists 存储,但(我敢说) Core Data 比 plists 更容易进行简单存储。
这里,我把下载进度百分比存储到模型中,但我不是反复存储该值。在快速连接中,你会得到一大堆进度变化(每秒数十次),且没必要每次都保存到 Core Data 中。我只在取消请求并希望在 UI 中看到下载的百分比时才保存数据。
存储数据到 Core Data 的另外一个好处是我们可以利用 NSFetchedResultsController 快速构建 DownloadsViewController。
错误处理
所有涉及网络的操作都有机会发生错误。大型文件的下载更是加剧这个机会。人们走出 Wi-Fi 范围,进入隧道,搭乘飞机以及其他大量事情都会干扰下载。为了确保最佳的用户体验,我要把它处理成允许用户快速重试(并在某些情况下,可以自动重试)。
当发生失败,把 DownloadInfo 的 state 标记为 .failed,UI 随之更新。单元格重载,用户可以点击重试该下载。
暂停和恢复
当 NSURLSession API 引入时,它就可以取消请求并产生一个不透明的对象,叫做 resume data。使用这种技术,你可以从它停下来的地方开始请求,唯一需要考虑的问题只是如何把该数据持久化,以备恢复下载。这样把该数据添加到我的 DownloadInfo 模型就很合适了。当用户点击正在进行的下载,会调用 downloadTask.cancel(byProducing:) 并保存 resume data 以供日后使用。
当下载开始,如果该模型存在 resume data,就会从上一次停止的位置继续下载。该功能很容易添加,但对大文件下载真的是超实用的。
其中要特别小心,参照 resume data is currently broken on iOS 10。
http://benscheirman.com/2016/09/resume-data-broken-in-ios-10/
处理蜂窝网络
我不希望任何数据的损坏,所以,默认设置 NSURLSessionConfiguration 的 allowsCellularAccess 设置为否。然后设置一个标识来切换该值状态,因为有些人可能希望无限制使用它。
使用 FX Reachability (https://github.com/nicklockwood/FXReachability)监听网络连接状态。如果用户希望在蜂窝网络下下载章节,我们弹出切换设置,让下载继续。
保持模型和文件同步
由于我们把文件的元数据保存在磁盘,所以需要确保这些元数据总是同步的。当你删除一个章节,必须同时删除磁盘上 Core Data 中的模型。为确保同步,我用 CleanupDownloadsOperation 检查磁盘上章节有对应的一个 DownloadInfo (除非数据被删除),并且每个下载文件都记录在 Core Data 中(除非被删除)。
在这里,如果万一有发生错误,两种状态(数据库/磁盘)必须取消同步。
后台下载
虽然这看似简单,后台下载会带来大量的复杂混乱的情况,下一篇会讲到这个主题。
他们说,只是加个离线下载嘛
当我最初想为 app 在发布前添加离线下载功能,但发现这需要额外一到两天的工作,这其中是相当的复杂。
相关文章
- python爬虫之下载文件的方式总结以及程序实例
- Android_(控件)使用ListView显示Android系统SD卡的文件列表_02
- vue blob流下载zip文件
- rsync+inotify文件同步
- 基于Hadoop的云盘系统客户端技术难点之二 HDFS文件访问控制
- 利用WGET下载文件,并保存到指定目录
- 重新点亮linux 命令树————文件列表查看命令[二]
- Linux中使用SecureCRT上传、下载文件命令sz与rz用法实例
- doc/jpg等文件在线浏览解决方案搭建
- 解决关于win7共享文件连接数限制的问题
- 【问题解决方案】git中的文件的重命名
- 如何将电脑中的文件彻底清除干净
- WCF HTTP 错误 404.3 - Not Found(由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。)
- SpringBoot整合oss实现文件的上传,查看,删除,下载
- c文件程序把玩
- Http服务器实现文件上传与下载(三)
- Python爬虫:使用requests库下载大文件
- Vue2.js:xlsx实现Excel文件的导入导出
- Pycharm中下载 requirement.txt 的文件并添加镜像源
- 2022年全国职业院校技能大赛“网络安全”竞赛试题文件上传渗透测试答案Flag
- PHP 生成.csv 文件并下载到浏览器
- 百度文库下载文件,没有财富的价值,音量不能下载
- 接口自动化测试 —— 文件上传/下载
- Android Studio添加文件注释头模板?
- 7. IIS短文件/文件夹漏洞(汇总整理)
- Dubbo上传文件之RestEasy
- setuid 粘滞位 ——想想passwd这个命令修改shadow文件就知道本质 当普通用户使用passwd更改自己密码的时候,那一瞬间突然灵魂附体了,实际在以passwd命令所有者root的身份在执行
- Spring4 MVC文件下载实例
- Linux上安装MGLtools并将pdb文件转为pdbqt文件
- 第六篇:终结,文件IO汇总