HTML5檔案上傳元件的深度剖析

edithfang發表於2014-07-20

HTML VS FLASH

對於檔案上傳,相信還有不少同學還停留在FLASH時代,其實現在 HTML5 不僅可以實現檔案上傳,而且可以做得更好。

以下是對 HTML5 與 FLASH 就檔案上傳方面的功能調研測試得出的結果。


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個請求是處於等待狀態,直到前面某個請求完成,才會開始有進度。

 為什麼併發會更快?
這裡列出了我個人覺得可能的原因。
  • 多請求能搶佔更多的頻寬。
  • 伺服器端可能針對單一請求限速。
  • 跨域時,併發可以共用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秒內完成序列號工作。
相關閱讀
評論(0)

相關文章