最近研究了一下關(guān)于文件下載的相關(guān)內(nèi)容,覺得還是寫些東西記下來比較好。起初只是想研究研究,但后來發(fā)現(xiàn)寫個(gè)可重用性比較高的模塊還是很有必要的,我想這也是大多數(shù)開發(fā)人員的習(xí)慣吧。
對于HTTP協(xié)議,向服務(wù)器請求某個(gè)文件時(shí),只要發(fā)送類似如下的請求即可:
GET /Path/FileName HTTP/1.0
Host: www.server.com:80
Accept: */*
User-Agent: GeneralDownloadApplication
Connection: close
每行用一個(gè)“回車換行”分隔,末尾再追加一個(gè)“回車換行”作為整個(gè)請求的結(jié)束。
第一行中的GET是HTTP協(xié)議支持的方法之一,方法名是大小寫敏感的,HTTP協(xié)議還支持OPTIONS、HAED、POST、PUT、DELETE、TRACE、CONNECT等方法,而GET和HEAD這兩個(gè)方法通常被認(rèn)為是“安全的”,也就是說任何實(shí)現(xiàn)了HTTP協(xié)議的服務(wù)器程序都會實(shí)現(xiàn)這兩個(gè)方法。對于文件下載功能,GET足矣。GET后面是一個(gè)空格,其后緊跟的是要下載的文件從WEB服務(wù)器根開始的絕對路徑。該路徑后又有一個(gè)空格,然后是協(xié)議名稱及協(xié)議版本。
除第一行以外,其余行都是HTTP頭的字段部分。Host字段表示主機(jī)名和端口號,如果端口號是默認(rèn)的80則可以不寫。Accept字段中的*/*表示接收任何類型的數(shù)據(jù)。User-Agent表示用戶代理,這個(gè)字段可有可無,但強(qiáng)烈建議加上,因?yàn)樗欠?wù)器統(tǒng)計(jì)、追蹤以及識別客戶端的依據(jù)。Connection字段中的close表示使用非持久連接。
關(guān)于HTTP協(xié)議更多的細(xì)節(jié)可以參考RFC2616(HTTP 1.1)。因?yàn)槲抑皇窍胪ㄟ^HTTP協(xié)議實(shí)現(xiàn)文件下載,所以也只看了一部分,并沒有看全。
如果服務(wù)器成功收到該請求,并且沒有出現(xiàn)任何錯誤,則會返回類似下面的數(shù)據(jù):
HTTP/1.0 200 OK
Content-Length: 13057672
Content-Type: application/octet-stream
Last-Modified: Wed, 10 Oct 2005 00:56:34 GMT
Accept-Ranges: bytes
ETag: "2f38a6cac7cec51:160c"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Wed, 16 Nov 2005 01:57:54 GMT
Connection: close
不用逐一解釋,很多東西一看幾乎就明白了,只說我們大家都關(guān)心內(nèi)容吧。
第一行是協(xié)議名稱及版本號,空格后面會有一個(gè)三位數(shù)的數(shù)字,是HTTP協(xié)議的響應(yīng)狀態(tài)碼,200表示成功,OK是對狀態(tài)碼的簡短文字描述。狀態(tài)碼共有5類:
1xx屬于通知類;
2xx屬于成功類;
3xx屬于重定向類;
4xx屬于客戶端錯誤類;
5xx屬于服務(wù)端錯誤類。
對于狀態(tài)碼,相信大家對404應(yīng)該很熟悉,如果向一個(gè)服務(wù)器請求一個(gè)不存在的文件,就會得到該錯誤,通常瀏覽器也會顯示類似“HTTP 404 - 未找到文件”這樣的錯誤。Content-Length字段是一個(gè)比較重要的字段,它標(biāo)明了服務(wù)器返回?cái)?shù)據(jù)的長度,這個(gè)長度是不包含HTTP頭長度的。換句話說,我們的請求中并沒有Range字段(后面會說到),表示我們請求的是整個(gè)文件,所以Content-Length就是整個(gè)文件的大小。其余各字段是一些關(guān)于文件和服務(wù)器的屬性信息。
這段返回?cái)?shù)據(jù)同樣是以最后一行的結(jié)束標(biāo)志(回車換行)和一個(gè)額外的回車換行作為結(jié)束,即“\r\n\r\n”。而“\r\n\r\n”后面緊接的就是文件的內(nèi)容了,這樣我們就可以找到“\r\n\r\n”,并從它后面的第一個(gè)字節(jié)開始,源源不斷的讀取,再寫到文件中了。
以上就是通過HTTP協(xié)議實(shí)現(xiàn)文件下載的全過程。但還不能實(shí)現(xiàn)斷點(diǎn)續(xù)傳,而實(shí)際上斷點(diǎn)續(xù)傳的實(shí)現(xiàn)非常簡單,只要在請求中加一個(gè)Range字段就可以了。
假如一個(gè)文件有1000個(gè)字節(jié),那么其范圍就是0-999,則:
Range: bytes=500-????? 表示讀取該文件的500-999字節(jié),共500字節(jié)。
Range: bytes=500-599?? 表示讀取該文件的500-599字節(jié),共100字節(jié)。
Range還有其它幾種寫法,但上面這兩種是最常用的,對于斷點(diǎn)續(xù)傳也足矣了。如果HTTP請求中包含Range字段,那么服務(wù)器會返回206(Partial Content),同時(shí)HTTP頭中也會有一個(gè)相應(yīng)的Content-Range字段,類似下面的格式:
Content-Range: bytes 500-999/1000
Content-Range字段說明服務(wù)器返回了文件的某個(gè)范圍及文件的總長度。這時(shí)Content-Length字段就不是整個(gè)文件的大小了,而是對應(yīng)文件這個(gè)范圍的字節(jié)數(shù),這一點(diǎn)一定要注意。
一切好像基本上沒有什么問題了,本來我也是這么認(rèn)為的,但事實(shí)并非如此。如果我們請求的文件的URL是類似http://www.server.com/filename.exe這樣的文件,則不會有問題。但是很多軟件下載網(wǎng)站的文件下載鏈接都是通過程序重定向的,比如pchome的ACDSee的HTTP下載地址是:
http://download.pchome.net/php/tdownload2.php?sid=5547&url=/multimedia/viewer/acdc31sr1b051007.exe&svr=1&typ=0
這種地址并沒有直接標(biāo)識文件的位置,而是通過程序進(jìn)行了重定向。如果向服務(wù)器請求這樣的URL,服務(wù)器就會返回302(Moved Temporarily),意思就是需要重定向,同時(shí)在HTTP頭中會包含一個(gè)Location字段,Location字段的值就是重定向后的目的URL。這時(shí)就需要斷開當(dāng)前的連接,而向這個(gè)重定向后的服務(wù)器發(fā)請求。
???? 好了,原理基本上就是這些了。其實(shí)裝個(gè)Sniffer好好分析一下,很容易就可以分析出來的。不過NetAnts也幫了我一些忙,它的文件下載日志對開發(fā)人員還是很有幫助的。
本文引自:http://hi.baidu.com/chinessnetstone/blog/item/603d20094009468ad0581b23.html