把大象裝進冰箱:HTTP傳輸大文件的方法
上次我們談到了HTTP報文裏的div,知道了HTTP可以傳輸很多種類的數據,不僅是文本,也能傳輸圖片,音頻和視頻。
早期互聯網上傳輸的基本上都是隻有幾k大小的文本和小圖片,現在的情況則大有不同。網頁裏包含的信息實在太多了,隨隨便便一個主頁HTML就有可能上百K,高質量的圖片都以M論,更不要說那些電影,電視劇了,幾G,幾十G都有可能。
相比之下,100M的光纖固網或者4G移動網路在這些大文件的壓力下都變成了小水管,無論是上傳還是下載,都會把網路傳輸鏈路擠的滿滿當當。
所以如何在有限的帶寬下高效快捷的傳輸這些大文件就成了一個重要的課題,這就好比是已經打開了冰箱門,該怎麼把大象塞進去在關上門呢?
下面我們就一起看看HTTP協議裏有哪些手段能解決這個問題。
數據壓縮
通常瀏覽器在發送請求時都會帶著Accept-Encoding頭欄位,裡面是瀏覽器支持的壓縮格式,例如gzip,deflate等,這樣伺服器就可以從中選擇一種壓縮演算法,放進Content-Encoding響應頭裡,在把原數據壓縮後發給瀏覽器。
如果壓縮率能有50%,也就是說100k的數據能夠壓縮成50k的大小,那麼就相當於在帶寬不變的情況下網速提升了一倍,加速的效果是非常明顯的。
不過這個解決方法也有個缺點,gzip等壓縮演算法通常只對文本文件有較好的壓縮率,而圖片,音頻視頻等多媒體數據本身已經是高度壓縮的,在用gzip處理也不會變小,所以它就失效了。
不過數據壓縮在處理文本的時候效果還是很好的,所以各大網站的伺服器都會使用這個手段作為保底,例如,在nginx裏就會使用gzip on指令,啟用對text/html的壓縮。
分塊傳輸
在數據壓縮之外,還能有什麼辦法來解決大文件的問題呢?
壓縮是把大文件整體變小,我們可以反過來思考,如果大文件整體不能變小,那就把它拆開,分解成多個小塊,把這些小塊分批發給瀏覽器,瀏覽器收到後在組裝復原。
這樣瀏覽器和伺服器都不用在內存裏保存文件的全部,每次只收發一小部分,網路也不會被大文件長時間佔用,內存,帶寬等資源也就節省下來了。
這種化整為零的思路在HTTP協議裏就是chunked分塊傳輸編碼,在響應報文裏用頭欄位Transfer-Encoding:chunked來表示,意思是報文裏的div部分不是一次性發過來的,而是分成了許多的塊(chunk)逐個發送。
分塊傳輸也可以用於流式數據,例如有資料庫動態生成的表單頁面,這種情況下div數據的長度是未知的,無法在頭欄位Content-Length給出確切的長度,所以也只能用chunked方式分塊發送。
Transfer-Encoding:chunked和Content-Length這兩個欄位是互斥的,也就是說響應報文裏這兩個欄位不能同時出現,一個響應報文裏的傳輸要麼是長度已知,要麼是長度未知。
下面是分塊傳輸的編碼規則:
1.每個分塊包含兩個部分,長度頭和數據塊
2.長度頭是以CRLF結尾的一行明文,用16進位數字表示長度
3.數據塊緊跟在長度頭後,最後也用CRLF結尾,但數據不包含CRLF
4.最後用一個長度為0的塊表示結束