HTTP協議的chunked編碼:
一般的HTTP Headers 應該包含Content-Length來指名報文的長度。
但是在有的時候,服務器無法確定HTTP回應的消息的大小,比如非常大的文件的下載,或者處理的邏輯比較復雜,需要一邊處理一邊實時生成消息(如果全部處理完再生成消息就會有很多缺點,比如用戶收到響應的時間就會很長),這個時候服務器一般都使用chunked編碼。此時,服務器不會帶上Content-Length這個響應頭,帶上了兩外一個頭:Transfer-encoding:chunked。
chunked編碼使用若干個Chunk組成,由一個標明長度為0的chunk結束,每個Chunk有兩部分組成,每個部分用回車換行隔開。在最后一個長度為0的Chunk中的內容是稱為footer的內容,是一些沒有寫的頭部內容。所以所謂的chunked編碼是如下的格式:
格式:
如果一個HTTP消息(請求消息或應答消息)的Transfer-Encoding消息頭的值為chunked,那么,消息體由數量未定的塊組成,並以最后一個大小為0的塊為結束。
每一個非空的塊都以該塊包含數據的字節數(字節數以十六進制表示)開始,跟隨一個CRLF (回車及換行),然后是數據本身,最后塊CRLF結束。在一些實現中,塊大小和CRLF之間填充有白空格(0x20)。
最后一塊是單行,由塊大小(0),一些可選的填充白空格,以及CRLF。最后一塊不再包含任何數據,但是可以發送可選的尾部,包括消息頭字段。消息最后以CRLF結尾。
第一個chunk數據的字節數+/r/n+第一塊chunk的數據 +/r/n+第二個chunk的數據的字節數+/r/n+第二塊chunk的數據+n個chunk+/r/n+0+/r/n。
因此,接收數據的時候,需要首先獲取每一個chunk數據的字節長度,然后,跳過2個字節(/r/n),取出數據,然后,再跳過2個字節(/r/n),獲取下一個chunk的長度,直到最后一個chunk,最后一個chunk一定是0,並且字節的長度都是十六進制形式傳輸,需要進行相應的轉化成十進制,如果是gzip格式的數據,那么,在最后完成所有數據組合之后,需要再解壓。
def decode_chunked(content): content = content.lstrip('\r') content = content.lstrip('\n') temp = content.find('\r\n') strtemp = content[0:temp] readbytes = int(strtemp, 16) newcont = '' start = 2 offset = temp + 2 newcont = '' while(readbytes > 0): newcont += content[offset:readbytes + offset] offset += readbytes endtemp = content.find('\r\n', offset + 2) if(endtemp > -1): strtemp = content[offset + 2:endtemp] readbytes = int(strtemp, 16) if(readbytes == 0): break else: offset = endtemp + 2 content = newcont return content def chunk_download(url, dest): fd = urllib.urlopen(url) chunk = decode_chunked(fd.read()) fd.close() f = open(dest, 'w') f.write(chunk) f.close()
部分內容參考:
http://zh.wikipedia.org/wiki/%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81
http://blog.csdn.net/xiqi8144/article/details/4413626
