移動(dòng)開(kāi)發(fā)中,為了減小包體積掉瞳,很多文件都會(huì)通過(guò)云端下發(fā)的方式服務(wù)用戶(hù)毕源。文件下載中浪漠,經(jīng)常會(huì)把Content-Length作為下載進(jìn)度的重要參數(shù),但是不同的服務(wù)器對(duì)待文件請(qǐng)求的方式不一樣霎褐,可能存在Content-Length為-1或不準(zhǔn)確的問(wèn)題址愿,導(dǎo)致下載進(jìn)度不準(zhǔn)確,影響用戶(hù)體驗(yàn)冻璃。
1.什么是Content-Length
在HTTP協(xié)議中响谓,Content-Length用于描述HTTP消息實(shí)體的傳輸長(zhǎng)度the transfer-length of the message-body。在HTTP協(xié)議中省艳,消息實(shí)體長(zhǎng)度和消息實(shí)體的傳輸長(zhǎng)度是有區(qū)別娘纷,比如說(shuō)gzip壓縮下,消息實(shí)體長(zhǎng)度是壓縮前的長(zhǎng)度拍埠,消息實(shí)體的傳輸長(zhǎng)度是gzip壓縮后的長(zhǎng)度失驶。
2.Content-Length為什么不靠譜
下面我們來(lái)分析幾種Content-Length的幾種異常情況:
2.1.gzip壓縮問(wèn)題
引用官方文檔的描述:
By default this implementation of HttpURLConnection requests that servers use gzip compression. Since getContentLength() returns the number of bytes transmitted, you cannot use that method to predict how many bytes can be read from getInputStream(). Instead, read that stream until it is exhausted: when read() returns -1. Gzip compression can be disabled by setting the acceptable encodings in the request header。
在默認(rèn)情況下HttpURLConnection 使用 gzip方式獲取枣购,文件 getContentLength() 這個(gè)方法嬉探,每次read完成后可以獲得,當(dāng)前已經(jīng)傳送了多少數(shù)據(jù)棉圈,而不能用這個(gè)方法獲取需要傳送多少字節(jié)的內(nèi)容涩堤,當(dāng)read() 返回 -1時(shí),讀取完成分瘾。
因此要取得正確的文件長(zhǎng)度胎围,要求http請(qǐng)求不要gzip壓縮。
conn .setRequestProperty("Accept-Encoding", "identity")
2.2.請(qǐng)求頭的問(wèn)題
一般可能是請(qǐng)求頭的問(wèn)題? 導(dǎo)致被服務(wù)器拒絕訪問(wèn)了
conn.setRequestProperty("User-Agent", " Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36")
2.3.服務(wù)器端無(wú)Content-Length
如果服務(wù)端沒(méi)有設(shè)置Content-Length, 那么客戶(hù)端獲取Content-Length時(shí)就是-1
3.http協(xié)議之Content-Length
對(duì)于http的請(qǐng)求返回結(jié)果要進(jìn)行內(nèi)容的長(zhǎng)度校驗(yàn)主要有兩種方式德召,二者互斥使用:
1.客戶(hù)端在http頭(head)加Connection:keep-alive時(shí)白魂,服務(wù)器的response是Transfer-Encoding:chunked的形式,通知頁(yè)面數(shù)據(jù)是否接收完畢上岗,例如長(zhǎng)連接或者程序運(yùn)行中可以動(dòng)態(tài)的輸出內(nèi)容福荸,例如一些運(yùn)算比較復(fù)雜且需要用戶(hù)及時(shí)的得到最新結(jié)果,那就采用chunked編碼將內(nèi)容分塊輸出肴掷。
2.除了如1所述之外的情況一般都是可以獲取到Content-Length的敬锐。
在具體的HTTP交互中,客戶(hù)端是如何獲取消息長(zhǎng)度的呢呆瞻,主要基于以下幾個(gè)規(guī)則:
1台夺、Content-Length如果存在并且有效的話(huà),則必須和消息內(nèi)容的傳輸長(zhǎng)度完全一致痴脾。(經(jīng)過(guò)測(cè)試颤介,如果過(guò)短則會(huì)截?cái)啵^(guò)長(zhǎng)則會(huì)導(dǎo)致超時(shí)。)
2买窟、如果存在Transfer-Encoding(重點(diǎn)是chunked)丰泊,則在header中不能有Content-Length薯定,有也會(huì)被忽視始绍。
3、如果采用短連接话侄,則直接可以通過(guò)服務(wù)器關(guān)閉連接來(lái)確定消息的傳輸長(zhǎng)度亏推。(這個(gè)很容易懂)
結(jié)合HTTP協(xié)議其他的特點(diǎn),比如說(shuō)Http1.1之前的不支持keep alive年堆。那么可以得出以下結(jié)論:
1吞杭、在Http 1.0及之前版本中,content-length字段可有可無(wú)变丧。
2芽狗、在http1.1及之后版本。如果是keep alive痒蓬,則content-length和chunk必然是二選一童擎。若是非keep alive,則和http1.0一樣攻晒。content-length可有可無(wú)
4.如何正確下載文件
引用官方文檔的話(huà):
read that stream until it is exhausted: when read() returns -1
numRead = bis.read(buffer, offset,BUFFER_SIZE - offset);
if (numRead == -1) {
// 已經(jīng)沒(méi)有數(shù)據(jù)了,退出循環(huán)
? ? if (offset > 0) {
? ? ? ? ? ? // buffer未填充滿(mǎn),但已經(jīng)沒(méi)數(shù)據(jù)了指黎,則寫(xiě)入文件
? fileOutputStream.write(buffer, 0, offset);
? ? }
? ? break;
}