HTML5文件上傳組件的深度剖析

前段時間在w3ctech技術交流會中分享了基於 HTML5 技術的文件上傳組件,因爲ppt攜帶的信息很是有限,故在此整理成文章分享出來,供感興趣的同窗閱讀。css

HTML VS FLASH

對於文件上傳,相信還有很多同窗還停留在FLASH時代,其實如今 HTML5 不只能夠實現文件上傳,並且能夠作得更好。html

如下是對 HTML5 與 FLASH 就文件上傳方面的功能調研測試得出的結果。html5

功能描述              FLASH   HTML5
  --------------------- ------- ------------
  文件多選              ✓       ✓
  格式過濾              ✓       ✓
  拖拽(文件 & 文件夾)   ✗       ✓
  截屏粘貼              ✗       ✓
  Cookie & Session      ✗       ✓
  文件內容讀取          ✓       ✓ 快150%
  圖片預覽&裁剪         ✓       ✓ 快200%
  文件上傳              ✓       ✓ 快10%
  進度跟蹤              ✓       ✓ 更加精準

PS: 截屏粘貼是指,若是剪切板裏面存在圖片數據,是能夠經過 CTRL + V 將此圖片做爲文件添加到文件上傳組件中的。讓剪切板中有圖片數據有不少方式:截屏軟件(如QQ截屏),瀏覽器中右擊圖片點擊複製,QQ聊天軟件中複製圖片...git

能夠看出,採用 HTML5 技術與傳統的 FLASH 技術相比,能實現的功能更多且性能優點特別明顯。github

更多調研細節請移步到這裏web

雖然 HTML5 優點很是明顯,可是若是目前支持 HTML5 的瀏覽器佔比狀況不理想,採用 HTML5 技術的文件上傳仍是不能帶來足夠的收益!算法

讓咱們先來看看由 TNW
提供的2014年3月份全球瀏覽器佔比圖。chrome

經過瀏覽器佔比能夠推算出,目前支持 HTML5 的瀏覽器佔比高達64.5%,加上 HTML5 有如此大的優點,看來徹底沒有理由拒絕採用 HTML5 來實現文件上傳了。canvas

可是還有35%的瀏覽器不支持 HTML5,怎麼辦?segmentfault

爲了考慮最大的兼容性,目前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的文件在不一樣的併發數下獲得的整體上傳時間對比圖。

併發對比圖

很明顯,併發數越多速度越快!

可是,併發數越多,給服務端帶來的壓力也越大,如何去選擇一個合適的併發數呢?

主要能夠從三方面去考慮。

  1. 併發數越多,服務端壓力越大,因此選擇併發數不能太大!
  2. 同時每一個瀏覽器都有固定的最大併發數限制,因此選擇併發數不能超出這個值。
  3. 從上面的圖標能夠看出,併發數到了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請求共用

左邊是非併發的狀況,右邊爲採用併發的狀況。能夠看出,當不採用併發的時候,每一個文件上傳請求前都會進行options請求驗證,而併發的時候,三個文件上傳請求共用了一個opions請求。

分塊上傳

爲何要採用分塊上傳?

試想一下,若是上傳的文件是一個大文件,原本上傳時間就相對較久,再加上網絡不穩定各類因素影響,容易致使傳輸中斷,用戶除了重傳整個文件外沒有更好的選擇。採用分片上傳能夠很好地解決這個問題。

什麼是分片上傳?

分塊上傳,就是把一個大的文件分紅若干塊,一塊一塊的傳輸。如上面的case, 若是傳輸中斷,僅需重傳出錯的分片,而不須要重傳整個文件,大大減小了重傳開銷。

那麼,採用分塊上傳還有哪些優點?

  • 更強容錯能力

    如以上的case, 出錯重傳,僅需重傳一小塊。

  • 能夠模擬暫停與繼續

    對於一個http請求,其實並無暫停功能,要不就是已完成,要不就是已中斷。若是不分塊,是無法作暫停功能。可是若是採用分塊上傳,在某個分塊上傳完了後不自動開始下個分塊上傳,是否是就實現了暫停功能?

    利用此功能,就能夠經過網絡檢測,在網絡斷開的時候自動暫停傳輸,網絡恢復後,繼續傳輸,給用戶帶來更好的體驗效果。

  • 利用併發提速

    若是單獨的上傳一個大文件,只有採用了分塊上傳才能利用併發請求來提速。

  • 更精準的速度跟蹤

    關於客戶端監控上傳進度,其實監控的都是客戶端的發送速度,服務端有沒有接收,有沒有存儲,是不知道的,只有在整個請求完畢,收到服務端反饋後才能肯定數據已經成功接收。這樣也就解釋了進度顯示的時候,長長出現,進度條瞬間到100%,要過一段時間才所有完成。若是採用分塊上傳,每一個分塊上傳完成,能夠肯定這個分塊服務端已經成功接收。

固然,分塊也會有必定的反作用,原本是一個請求,分塊後變成了多個請求,天然會帶來網絡開銷。那麼具體是個什麼的狀況呢?

如下是經過測試3個30M的文件在3個併發下調整不一樣的分片大小得出的整體時間消耗表。

能夠看出來,分塊越小,時間消耗越大,尤爲是分塊大小小到256K的時候,時間花費增加特別明顯。

那麼,如何選擇一個合適的分塊大小?

主要有三個方面的考慮。

  • 分塊越小,請求越多,開銷越大。因此不能設置得過小。
  • 分塊越大,靈活度越小,前面所說的那些優點就會相對越不明顯。故不能過小。
  • 服務端通常都會有個固定大小的接受buffer(clientbodybuffer_size), 分塊的大小最好是這個值的整數倍。

綜合這些考慮,推薦的分塊大小是2M-5M,具體size根據產品中文件上傳的大小分佈來定。若是上傳的文件大部分是500M以上,很大的文件,建議是5M, 若是相對較小,推薦2M。

斷點續傳

有了分塊上傳,其實咱們能夠實現更多的功能。試想,若是服務端可以把每一個已成功上傳的分塊都記住,客戶端能夠快速驗證,條件選擇跳過某個分塊,是否是就實現了斷點續傳?

那麼,斷點續傳能帶來哪些好處?

  1. 節省流量,避免上傳重複的分塊。
  2. 減小用戶等待時間。
  3. 可恢復的上傳。出現中斷,就算瀏覽器刷新或者是換了臺電腦也能恢復到上次中斷的位置。

那麼如今最關鍵的問題是如何標識一個分塊了。怎樣標識讓服務端好入庫,同時客戶端能夠快速的計算出來與服務端驗證,換句話說就是,如何去找出分塊的惟一ID。

以前嘗試過文件名+分塊編號,或者再加文件大小,文件最後修改時間什麼的,都沒法保證分塊的惟一性。因而仍是直接採用 MD5 的方式來序列化文件內容吧,這樣就算是文件不一樣名,只要內容是一致的也會正確地識別出是同一個分塊。

那麼如今的邏輯就是,每次分塊上傳前,根據內容 MD5 序列化,獲得一個惟一ID,與服務端驗證,若是此分塊已經存在於服務端,則直接跳過此分塊上傳,不然上傳此分塊,成功後,服務端記下此分塊ID。

如是,分塊上傳是否是具備了秒傳的功能?既然分塊能秒傳,那麼整個文件是否也能夠秒傳?

秒傳

分塊能秒傳,整個文件固然也能秒傳,關鍵仍是看 MD5 的性能。

經過以上測試結果能夠看出,若是文件大小在 10M 之內,是能夠真正的秒級內完成的,大於這個值時間花費就大於1秒了,好比一個200M的文件 MD5 時間花費須要13秒左右。

可是,即使如此,相比於文件傳輸時間花費,MD5 的時間花費根本就不算什麼。對於相似於百度雲,文庫這類的產品,在上傳一個文件的時候極可能服務端已經存在了此文件,若是多等個幾秒鐘,能跳過整個文件上傳,我以爲是很是划算的。

另外,若是是一次上傳多個文件是能夠在算法上去優化這個過程的。

  • 驗證過程提早到當前文件的傳輸期

    若是當前文件已經在傳輸了,這個時候,用戶是處於等待狀態,機器也處於等待期,若是把下一個文件的驗證過程移至此過程,那麼用戶的等待 MD5 的時間和等待當前文件傳輸完成的時間就重合了。這樣用戶就只須要等待第一個文件的驗證過程。

  • 小文件優先處理,減小用戶等待時間

    由於第一個文件的驗證等待沒法避免,若是第一個文件處理的文件越小,是否是等待的時間就越短?因此把隊列中最小的一個文件放到第一個優先處理能夠進一步減小用戶等待時間

  • 更換序列化算法,取段MD5

    其實對於某些二進制文件,如JPEG,前面一段數據記錄了不少此圖片的信息,好比:拍攝時間,相機名稱,圖片尺寸,圖片旋轉度等等,直接 MD5 這一段數據基本上就能夠保證此文件的惟一性了。只要取段的總大小小於10M,再大的文件也能在1秒內完成序列號工做。

參考資料


by 2betop
via 百度 FEX

相關文章
相關標籤/搜索