设计一个健壮的大型文件下载系统
轉至簡書作者: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 在發布前添加離線下載功能,但發現這需要額外一到兩天的工作,這其中是相當的復雜。
?
總結
以上是生活随笔為你收集整理的设计一个健壮的大型文件下载系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android KK版本号收到短信后,点
- 下一篇: SpringMVC 简单拦截器配置