做過客戶端檔案上傳的同學會明白,基於HTTP的檔案上傳並沒有看起來那麼簡單。按我過去經驗,至少有兩塊工作會比看上去要麻煩一些,第一個是斷點續傳,第二個是進度展示。斷點續傳想要優化的好要花不少力氣,後面有機會再寫,這篇先看上傳進度的問題。
首先說現象:我們呼叫第三方http framework上傳檔案的時候,都會有API回撥告訴我們上傳的具體進度,這個進度值都是不準的。
比如下面程式碼使用alamofire(使用AFNetworking也一樣)標準API上傳一個圖片到伺服器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
func doUpload() { let fileURL = Bundle.main.url(forResource: "test", withExtension: "png") var sentUnitCount: Int64 = 0 var start: CFTimeInterval = 0 var end: CFTimeInterval = 0 var cur: CFTimeInterval = CACurrentMediaTime() Alamofire.upload(fileURL!, to: "https://httpbin.org/post") .uploadProgress { progress in print("Upload Progress: \(progress.fractionCompleted), sent bytes: \(progress.completedUnitCount-sentUnitCount), totoal sent bytes: \(progress.completedUnitCount/1024)") sentUnitCount = progress.completedUnitCount if progress.fractionCompleted == 1 { start = CACurrentMediaTime(); } let now = CACurrentMediaTime() print("elapsed: \(now - cur)") cur = now } .response { (rsp) in end = CACurrentMediaTime(); print("\(end - start)") } } |
上面的程式碼會上傳一個大約20KB的圖片到目標伺服器,如果執行程式碼,你會發現uploadProgress會在一瞬間到達100%,無論裝置的網路是快還是慢(大小為100KB的圖片也一樣)。我們當然知道一個20KB的圖片不可能瞬間抵達伺服器,那麼這個進度到底準不準,問題在哪呢?
要理解這個uploadProgress的含義,需要理解tcp協議傳送資料的方式,略去一些協議的細節,大致的模型可以用下圖表示:
我們的客戶端(TCP的傳送方)會有一個send buffer,這個send buffer會快取等待傳送的資料,alamofire等第三方庫會先將資料持續寫入這個send buffer,每寫一次就會呼叫一次uploadProgress,實際上這時候資料還老老實實待在send buffer當中,並沒有抵達伺服器,所以會有上面一呼叫upload函式進度瞬間到100%的現象,這時候如果你根據progress展示進度條,使用者就會發現進度條瞬間跳到100%,之後一直卡住,直到send buffer當中的資料全部真正抵達Server並被Server Ack之後,才會進入response回撥。
我拿自己的iPhone6測試1MB大小的檔案,alamofire每寫4KB資料會進入一次uploadProgress回撥,一直寫到大概120KB左右才停住,等這120KB傳輸完成之後,再啪啪啪寫入後面的100多KB。
所以根據上面的分析,如果你對檔案上傳不做任何優化,會有兩個奇怪的現象:
- 幾十KB的小檔案會在一瞬間progress到100%。
- 進度跳到100%之後,會卡住幾秒(時長因網路狀況而異)再進入response回撥。
這種場景當然需要優化,怎麼優化呢?有套路的,用個假的進度條就可以了。
解決方案
先不要管上傳的進度,做個步調優雅的進度動畫,慢慢兒的先滑到50%,看下 if 這時候上傳進度有50%,繼續往前滑到75%,else 沒到50%就繼續等上傳進度,如此往復直達100%。為了避免100%之後還要等最後send buffer清空,可以在99%的位置等一等。這種假進度條的套路在瀏覽器載入網頁的時候也會用到,使用過微信開啟網頁等載入完成的就能明白。這個套路廣泛的應用於無法獲取真實進度,卻不得不給產品經理或者使用者一個交代的場景。
這個上傳進度的坑你踩過沒呢?
課後作業:根據上面的分析,下載的進度準不準呢?
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!