請求頭一:
>>>>>>>>>>>>>>>>>>>>>>>>
range:bytes=1024- //斷點續傳請求必須包含該請求頭
host:192.168.118.120:8888
accept:*/*
>>>>>>>>>>>>>>>>>>>>>>>>
響應頭一:
>>>>>>>>>>>>>>>>>>>>>>>>
Server: Apache-Coyote/1.1
Content-Disposition: attachment; filename=WebGoat-OWASP_Developer-5.2.zip
Accept-Ranges: bytes
Content-Range: bytes 1024-304974591/304974592
Content-Type: application/x-download;charset=utf-8
Content-Length: 304973568 //須要特別注意這裏長度值爲請求須要的長度,即304974591 - 1024
>>>>>>>>>>>>>>>>>>>>>>>>
請求頭二:
>>>>>>>>>>>>>>>>>>>>>>>>
range:bytes=10-1033 //斷點續傳請求必須包含該請求頭
host:192.168.118.120:8888
accept:*/*
>>>>>>>>>>>>>>>>>>>>>>>>
響應頭二:
>>>>>>>>>>>>>>>>>>>>>>>>
Server: Apache-Coyote/1.1
Content-Disposition: attachment; filename=WebGoat-OWASP_Developer-5.2.zip
Accept-Ranges: bytes
Content-Range: bytes 10-1033/304974592
Content-Type: application/x-download;charset=utf-8
Content-Length: 1024 //須要特別注意這裏長度值爲請求須要的長度,即1033- 10
>>>>>>>>>>>>>>>>>>>>>>>>html
/**
* 下載服務器已存在的文件,支持斷點續傳
*
* @param request
* 請求對象
* @param response
* 響應對象
* @param path
* 文件路徑(絕對)
*/
public static void download(HttpServletRequest request, HttpServletResponse response, File proposeFile) {
LOGGER.debug("下載文件路徑:" + proposeFile.getPath());
InputStream inputStream = null;
OutputStream bufferOut = null;
try {
// 設置響應報頭
long fSize = proposeFile.length();
response.setContentType("application/x-download");
// Content-Disposition: attachment; filename=WebGoat-OWASP_Developer-5.2.zip
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(proposeFile.getName(), ENCODING));
// Accept-Ranges: bytes
response.setHeader("Accept-Ranges", "bytes");
long pos = 0, last = fSize - 1, sum = 0;//pos開始讀取位置; last最後讀取位置; sum記錄總共已經讀取了多少字節
if (null != request.getHeader("Range")) {
// 斷點續傳
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
try {
// 情景一:RANGE: bytes=2000070- 情景二:RANGE: bytes=2000070-2000970
String numRang = request.getHeader("Range").replaceAll("bytes=", "");
String[] strRange = numRang.split("-");
if (strRange.length == 2) {
pos = Long.parseLong(strRange[0].trim());
last = Long.parseLong(strRange[1].trim());
} else {
pos = Long.parseLong(numRang.replaceAll("-", "").trim());
}
} catch (NumberFormatException e) {
LOGGER.error(request.getHeader("Range") + " is not Number!");
pos = 0;
}
}
long rangLength = last - pos + 1;// 總共須要讀取的字節
// Content-Range: bytes 10-1033/304974592
String contentRange = new StringBuffer("bytes ").append(pos).append("-").append(last).append("/").append(fSize).toString();
response.setHeader("Content-Range", contentRange);
// Content-Length: 1024
response.addHeader("Content-Length", String.valueOf(rangLength));
// 跳過已經下載的部分,進行後續下載
bufferOut = new BufferedOutputStream(response.getOutputStream());
inputStream = new BufferedInputStream(new FileInputStream(proposeFile));
inputStream.skip(pos);
byte[] buffer = new byte[1024];
int length = 0;
while (sum < rangLength) {
length = inputStream.read(buffer, 0, ((rangLength - sum) <= buffer.length ? ((int) (rangLength - sum)) : buffer.length));
sum = sum + length;
bufferOut.write(buffer, 0, length);
}
} catch (Throwable e) {
if (e instanceof ClientAbortException) {
// 瀏覽器點擊取消
LOGGER.info("用戶取消下載!");
} else {
LOGGER.info("下載文件失敗....");
e.printStackTrace();
}
} finally {
try {
if (bufferOut != null) {
bufferOut.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
開發中遇到的一個錯誤提示:java
org.apache.catalina.connector.ClientAbortException: Connection reset by peer: socket write errorapache
該錯誤的緣由就是由於上面的Content-Length: 1024 與請求頭重請求的長度不一致,致使了請求端拒絕了編程
http斷點續傳原理:http頭 Range、Content-Range瀏覽器
所謂斷點續傳,也就是要從文件已經下載的地方開始繼續下載。在之前版本的 HTTP 協議是不支持斷點的,HTTP/1.1 開始就支持了。通常斷點下載時纔用到 Range 和 Content-Range 實體頭。服務器
Range 網絡
用於請求頭中,指定第一個字節的位置和最後一個字節的位置,通常格式:app
Range:(unit=first byte pos)-[last byte pos] dom
Content-Rangesocket
用於響應頭,指定整個實體中的一部分的插入位置,他也指示了整個實體的長度。在服務器向客戶返回一個部分響應,它必須描述響應覆蓋的範圍和整個實體長度。通常格式:
Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
請求下載整個文件:
通常正常回應
如下是摘取網絡中的一段內容,並進了修改:原始的內容有誤致使被坑
斷點續傳的原理
其實斷點續傳的原理很簡單,就是在 Http 的請求上和通常的下載有所不一樣而已。
打個比方,瀏覽器請求服務器上的一個文時,所發出的請求以下:
假設服務器域名爲 wwww.sjtu.edu.cn,文件名爲 down.zip。
GET /down.zip HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive
服務器收到請求後,按要求尋找請求的文件,提取文件的信息,而後返回給瀏覽器,返回信息以下:
200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon, 30 Apr 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT
所謂斷點續傳,也就是要從文件已經下載的地方開始繼續下載。因此在客戶端瀏覽器傳給 Web 服務器的時候要多加一條信息 -- 從哪裏開始。
下面是用本身編的一個"瀏覽器"來傳遞請求信息給 Web 服務器,要求從 2000070 字節開始。
GET /down.zip HTTP/1.0
User-Agent: NetFox
RANGE: bytes=2000070-
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔細看一下就會發現多了一行 RANGE: bytes=2000070-
這一行的意思就是告訴服務器 down.zip 這個文件從 2000070 字節開始傳,前面的字節不用傳了。
服務器收到這個請求之後,返回的信息以下:
206
Content-Length=106585958
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
和前面服務器返回的信息比較一下,就會發現變化:
Content-Length=106585958
Content-Range=bytes 2000070-106786027/106786028
返回的代碼也改成 206 了,而再也不是 200 了。
知道了以上原理,就能夠進行斷點續傳的編程了
從輸入流中取出的字節流就是 down.zip 文件從 2000070 開始的字節流。 你們看,其實斷點續傳用 Java 實現起來仍是很簡單的吧。 接下來要作的事就是怎麼保存得到的流到文件中去了。
怎麼樣,也很簡單吧。 接下來要作的就是整合成一個完整的程序了。包括一系列的線程控制等等。
注:轉載http://www.ibm.com/developerworks/cn/java/joy-down/index.html