HTML VS FLASH
對于檔案上傳,相信還有不少同學還停留在FLASH時代,其實作在 HTML5 不僅可以實作檔案上傳,而且可以做得更好。
以下是對 HTML5 與 FLASH 就檔案上傳方面的功能調研測試得出的結果。
功能描述 FLASH HTML5
--------------------- ------- ------------
檔案多選 ✓ ✓
格式過濾 ✓ ✓
拖拽(檔案 & 檔案夾) ✗ ✓
截屏粘貼 ✗ ✓
Cookie & Session ✗ ✓
檔案内容讀取 ✓ ✓ 快150%
圖檔預覽&裁剪 ✓ ✓ 快200%
檔案上傳 ✓ ✓ 快10%
進度跟蹤 ✓ ✓ 更加精準
PS: 截屏粘貼是指,如果剪切闆裡面存在圖檔資料,是可以通過 CTRL + V 将此圖檔作為檔案添加到檔案上傳元件中的。讓剪切闆中有圖檔資料有很多方式:截屏軟體(如QQ截屏),浏覽器中右擊圖檔點選複制,QQ聊天軟體中複制圖檔...
可以看出,采用 HTML5 技術與傳統的 FLASH 技術相比,能實作的功能更多且性能優勢特别明顯。
更多調研細節請移步到
這裡。
雖然 HTML5 優勢非常明顯,但是如果目前支援 HTML5 的浏覽器占比情況不理想,采用 HTML5 技術的檔案上傳還是不能帶來足夠的收益!
讓我們先來看看由
TNW提供的2014年3月份全球浏覽器占比圖。

通過浏覽器占比可以推算出,目前支援 HTML5 的浏覽器占比高達64.5%,加上 HTML5 有如此大的優勢,看來完全沒有理由拒絕采用 HTML5 來實作檔案上傳了。
但是還有35%的浏覽器不支援 HTML5,怎麼辦?
為了考慮最大的相容性,目前
WebUploader同時實作了 HTML5 和 FLASH 兩套運作時,在不支援 HTML5 的浏覽器裡面自動切換成 FLASH 方式上傳,這樣的好處是,既能在條件允許的情況保證 HTML5 發揮出其高效的優勢,又能在不支援 HTML5 的浏覽器裡面保證能正常運作。
如何優化檔案上傳性能?
對于檔案上傳性能優化,可以從兩個方面來着手,即上傳前的優化和上傳過程中的優化。
上傳前的優化
主要有兩個思路。
- 思路一:通過減少檔案體積,減少上傳流量來優化。
- 思路二:通過合并小檔案,減少請求數來優化。
基于這兩個思路,我們嘗試了以下幾種方案。
-
圖檔壓縮
如果上傳的是圖檔類檔案,存在一部分使用者喜歡直接選擇相機或者手機拍攝的原始檔案進行上傳,對于這類圖檔,圖檔的分辨極高,高達5000多。這就注定了此圖檔的檔案體積不會太小。其實如果隻是在電腦上檢視,1千多的分辨率就已經足夠。于是我們嘗試通過js将此類圖檔進行縮小,得出的結果是:1張(5184×3456)大小為5M的jpg圖檔,縮小到(1600x1600)後,體積變成了407kb, 直接節省了4.5M的流量。
-
ZIP 合并小檔案
類似于拷貝檔案夾到U盤,如果小檔案比較多,拷貝過程非常慢,通常我們的做法是将檔案夾打包成一個檔案再拷貝,速度往往要快出許多。其實檔案上傳也一樣,如果能把體積比較小的檔案合并成一個檔案,請求數就會少了很多,這樣是不是就提高了整體檔案上傳速度呢?
但是,通過測試得出的結果是不盡如意的。ZIP的運算效率太低,以至于隻有在2G網絡下才有速度提升,3G和wifi網絡下反而變慢了。更多細節請移步到
-
SPRITE 合并小圖檔
類似于css sprite, 将多個小圖檔畫到一張大圖檔上,通過這種方式來進行合并,思路和zip合并差不多,但是采用的技術不太一樣,此方案是直接用canvas來畫,經過測試發現速度比zip快出來10倍,總體能帶來20%左右的速度提升。
但是此方案隻滿足于圖檔類檔案,且服務端需要通過位置資訊還原出小檔案,帶來一定的服務端開發成本,目前并沒有采用此方案。更多細節請移步到
-
直接合并檔案内容
其實,并不需要采用ZIP或者SPRIT方式合并檔案,把檔案讀取出 arraybuffer 後是直接可以連接配接在一起的,之後還可以再次轉成 blob 發送到服務端,或者直接發送 arraybuffer,理論上性能應該比SPRITE方案靠譜。
但是這塊沒有進行詳細的調研,在此就不多說了。
上傳過程中優化
主要采用并發與分塊,以下将細說這兩個方案。
并發上傳
采用此方案主要是源于單一請求無法讓網絡達到飽和,于是我們來嘗試采用并發方式看能否提高總體檔案上傳速度。
以下是通過測試20個1M的檔案在不同的并發數下得到的總體上傳時間對比圖。
很明顯,并發數越多速度越快!
但是,并發數越多,給服務端帶來的壓力也越大,如何去選擇一個合适的并發數呢?
主要可以從三方面去考慮。
- 并發數越多,服務端壓力越大,是以選擇并發數不能太大!
- 同時每個浏覽器都有固定的最大并發數限制,是以選擇并發數不能超出這個值。
- 從上面的圖示可以看出,并發數到了3以後,收益開始漸漸不明顯。
如是,最佳的并發數應該是3。
PS: 以下是常用浏覽器的最大并發數資訊。根據這個表可以說明為什麼前面的測試結果,并發數隻測試到了6,原因是chrome的最大并發數是6,當并發設定到7的時候,第7個請求是處于等待狀态,直到前面某個請求完成,才會開始有進度。
浏覽器 HTTP 1.1 HTTP 1.0
------------- ---------- ----------
IE 6, 7 2 4
IE 8 6 6
Firefox2 2 8
Firefox3 6 6
Safari 3, 4 4 4
Chrome 1, 2 6 ?
Chrome 3 4 4
Chrome 4+ 6 ?
為什麼并發會更快?
這裡列出了我個人覺得可能的原因。
- 多請求能搶占更多的帶寬。
- 伺服器端可能針對單一請求限速。
- 跨域時,并發可以共用options驗證請求,浏覽器有個特點是,對于跨域請求,如果在一定的時間内,有多個請求,隻會發送一個options請求驗證的。
左邊是非并發的情況,右邊為采用并發的情況。可以看出,當不采用并發的時候,每個檔案上傳請求前都會進行options請求驗證,而并發的時候,三個檔案上傳請求共用了一個opions請求。
分塊上傳
為什麼要采用分塊上傳?
試想一下,如果上傳的檔案是一個大檔案,本來上傳時間就相對較久,再加上網絡不穩定各種因素影響,容易導緻傳輸中斷,使用者除了重傳整個檔案外沒有更好的選擇。采用分片上傳可以很好地解決這個問題。
什麼是分片上傳?
分塊上傳,就是把一個大的檔案分成若幹塊,一塊一塊的傳輸。如上面的case, 如果傳輸中斷,僅需重傳出錯的分片,而不需要重傳整個檔案,大大減少了重傳開銷。
那麼,采用分塊上傳還有哪些優勢?
-
更強容錯能力
如以上的case, 出錯重傳,僅需重傳一小塊。
-
可以模拟暫停與繼續
對于一個http請求,其實并沒有暫停功能,要不就是已完成,要不就是已中斷。如果不分塊,是沒法做暫停功能。但是如果采用分塊上傳,在某個分塊上傳完了後不自動開始下個分塊上傳,是不是就實作了暫停功能?
利用此功能,就可以通過網絡檢測,在網絡斷開的時候自動暫停傳輸,網絡恢複後,繼續傳輸,給使用者帶來更好的體驗效果。
-
利用并發提速
如果單獨的上傳一個大檔案,隻有采用了分塊上傳才能利用并發請求來提速。
-
更精準的速度跟蹤
關于用戶端監控上傳進度,其實監控的都是用戶端的發送速度,服務端有沒有接收,有沒有存儲,是不知道的,隻有在整個請求完畢,收到服務端回報後才能确定資料已經成功接收。這樣也就解釋了進度顯示的時候,長長出現,進度條瞬間到100%,要過一段時間才全部完成。如果采用分塊上傳,每個分塊上傳完成,可以确定這個分塊服務端已經成功接收。
當然,分塊也會有一定的副作用,本來是一個請求,分塊後變成了多個請求,自然會帶來網絡開銷。那麼具體是個什麼的情況呢?
以下是通過測試3個30M的檔案在3個并發下調整不同的分片大小得出的總體時間消耗表。
可以看出來,分塊越小,時間消耗越大,尤其是分塊大小小到256K的時候,時間花費增長特别明顯。
那麼,如何選擇一個合适的分塊大小?
主要有三個方面的考慮。
- 分塊越小,請求越多,開銷越大。是以不能設定得太小。
- 分塊越大,靈活度越小,前面所說的那些優勢就會相對越不明顯。故不能太小。
- 服務端一般都會有個固定大小的接受buffer(clientbodybuffer_size), 分塊的大小最好是這個值的整數倍。
綜合這些考慮,推薦的分塊大小是2M-5M,具體size根據産品中檔案上傳的大小分布來定。如果上傳的檔案大部分是500M以上,很大的檔案,建議是5M, 如果相對較小,推薦2M。
斷點續傳
有了分塊上傳,其實我們可以實作更多的功能。試想,如果服務端能夠把每個已成功上傳的分塊都記住,用戶端可以快速驗證,條件選擇跳過某個分塊,是不是就實作了斷點續傳?
那麼,斷點續傳能帶來哪些好處?
- 節省流量,避免上傳重複的分塊。
- 減少使用者等待時間。
- 可恢複的上傳。出現中斷,就算浏覽器重新整理或者是換了台電腦也能恢複到上次中斷的位置。
那麼現在最關鍵的問題是如何辨別一個分塊了。怎樣辨別讓服務端好入庫,同時用戶端可以快速的計算出來與服務端驗證,換句話說就是,如何去找出分塊的唯一ID。
之前嘗試過檔案名+分塊編号,或者再加檔案大小,檔案最後修改時間什麼的,都無法保證分塊的唯一性。于是還是直接采用 MD5 的方式來序列化檔案内容吧,這樣就算是檔案不同名,隻要内容是一緻的也會正确地識别出是同一個分塊。
那麼現在的邏輯就是,每次分塊上傳前,根據内容 MD5 序列化,得到一個唯一ID,與服務端驗證,如果此分塊已經存在于服務端,則直接跳過此分塊上傳,否則上傳此分塊,成功後,服務端記下此分塊ID。
如是,分塊上傳是不是具有了秒傳的功能?既然分塊能秒傳,那麼整個檔案是否也可以秒傳?
秒傳
分塊能秒傳,整個檔案當然也能秒傳,關鍵還是看 MD5 的性能。
通過以上測試結果可以看出,如果檔案大小在 10M 以内,是可以真正的秒級内完成的,大于這個值時間花費就大于1秒了,比如一個200M的檔案 MD5 時間花費需要13秒左右。
但是,即便如此,相比于檔案傳輸時間花費,MD5 的時間花費根本就不算什麼。對于類似于百度雲,文庫這類的産品,在上傳一個檔案的時候很可能服務端已經存在了此檔案,如果多等個幾秒鐘,能跳過整個檔案上傳,我覺得是非常劃算的。
另外,如果是一次上傳多個檔案是可以在算法上去優化這個過程的。
-
驗證過程提前到目前檔案的傳輸期
如果目前檔案已經在傳輸了,這個時候,使用者是處于等待狀态,機器也處于等待期,如果把下一個檔案的驗證過程移至此過程,那麼使用者的等待 MD5 的時間和等待目前檔案傳輸完成的時間就重合了。這樣使用者就隻需要等待第一個檔案的驗證過程。
-
小檔案優先處理,減少使用者等待時間
因為第一個檔案的驗證等待無法避免,如果第一個檔案處理的檔案越小,是不是等待的時間就越短?是以把隊列中最小的一個檔案放到第一個優先處理可以進一步減少使用者等待時間
-
更換序列化算法,取段MD5
其實對于某些二進制檔案,如JPEG,前面一段資料記錄了很多此圖檔的資訊,比如:拍攝時間,相機名稱,圖檔尺寸,圖檔旋轉度等等,直接 MD5 這一段資料基本上就可以保證此檔案的唯一性了。隻要取段的總大小小于10M,再大的檔案也能在1秒内完成序列号工作。