Java servlet 簡單實現http檔案下載斷點續傳功能
斷點續傳,聽上去似乎是個比較高階的話題,本文只講述一下http版的斷點續傳,其他協議的大家可以自行研究。
http協議中,服務端實現斷點續傳首先需要讀取客戶端傳送的Range頭資訊,比如“Range: bytes=12583394-”這個就是指原來正在下載的檔案需要從第12583394位元組繼續下載,然後我們利用java.io.File的skip方法,捨棄掉原檔案的前n個位元組,接著就繼續慢慢write吧。。。
但是客戶端又是如何判斷服務端是否支援斷點續傳的呢?主要就是Accept-Ranges和Content-Length頭資訊。比如“Accept-Ranges:bytes”和“Content-Length:99999999
然後需要注意的是,假如客戶端剛才由於某些原因,暫停了下載,現在恢復的時候,就會如前所述,傳來Range頭資訊,這時候,我們的response就需要設定一下狀態碼,這裡應該設定成206(詳細解釋請看http://en.wikipedia.org/wiki/List_of_HTTP_status_codes),還有就是Content-Range頭資訊,格式為“bytes x-(y-1)/y”,x就是客戶端傳來的開始位元組位置,y就是檔案長度。
理解了這些,再看看給出的例項程式碼,實現起來就是十分簡單了。例項程式碼:
01 |
package test; |
02 |
03 |
import java.io.File; |
04 |
import java.io.FileInputStream; |
05 |
import java.io.IOException; |
06 |
import java.io.OutputStream; |
07 |
08 |
import javax.servlet.ServletException; |
09 |
import javax.servlet.http.HttpServlet; |
10 |
import javax.servlet.http.HttpServletRequest; |
11 |
import javax.servlet.http.HttpServletResponse; |
12 |
13 |
public class
DownloadTestServlet extends
HttpServlet { |
14 |
@Override |
15 |
protected
void doGet(HttpServletRequest req, HttpServletResponse resp) |
16 |
throws
ServletException, IOException { |
17 |
// TODO Auto-generated method stub |
18 |
doPost(req, resp); |
19 |
} |
20 |
21 |
@Override |
22 |
protected
void doPost(HttpServletRequest req, HttpServletResponse resp) |
23 |
throws
ServletException, IOException { |
24 |
resp.reset(); |
25 |
long
pos = 0 ; |
26 |
String fileName = req.getParameter( "file" ); |
27 |
System.out.println( "The file is:"
+ fileName); |
28 |
OutputStream os =
null ; |
29 |
FileInputStream is =
null ; |
30 |
try
{ |
31 |
File f =
new File( "D:\\xx\\"
+ fileName); |
32 |
is =
new FileInputStream(f); |
33 |
long
fSize = f.length(); |
34 |
byte
xx[] = new
byte [ 4096 ]; |
35 |
resp.setHeader( "Accept-Ranges" ,
"bytes" ); |
36 |
resp.setHeader( "Content-Length" , fSize +
"" ); |
37 |
resp.setHeader( "Content-Disposition" ,
"attachment;filename=" |
38 |
+ fileName); |
39 |
if
(req.getHeader( "Range" ) !=
null ) { |
40 |
// 若客戶端傳來Range,說明之前下載了一部分,設定206狀態(SC_PARTIAL_CONTENT) |
41 |
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); |
42 |
pos = Long.parseLong(req.getHeader( "Range" ) |
43 |
.replaceAll( "bytes=" ,
"" ).replaceAll( "-" ,
"" )); |
44 |
} |
45 |
if
(pos != 0 ) { |
46 |
String contentRange =
new StringBuffer( "bytes " ) |
47 |
.append( new
Long(pos).toString()).append( "-" ) |
48 |
.append( new
Long(fSize - 1 ).toString()).append( "/" ) |
49 |
.append( new
Long(fSize).toString()).toString(); |
50 |
resp.setHeader( "Content-Range" , contentRange); |
51 |
System.out.println( "Content-Range="
+ contentRange); |
52 |
// 略過已經傳輸過的位元組 |
53 |
is.skip(pos); |
54 |
} |
55 |
os = resp.getOutputStream(); |
56 |
boolean
all = false ; |
57 |
while
(!all) { |
58 |
int
n = is.read(xx); |
59 |
if
(n != - 1 ) { |
60 |
os.write(xx,
0 , n); |
61 |
}
else { |
62 |
all =
true ; |
63 |
} |
64 |
} |
65 |
}
catch (IOException e) { |
66 |
e.printStackTrace(); |
67 |
return ; |
68 |
}
finally { |
69 |
if
(is != null ) |
70 |
is.close(); |
71 |
if
(os != null ) |
72 |
os.close(); |
73 |
} |
74 |
} |
75 |
} |
本例項程式碼使用ff測試通過,但用360和the world等自帶的下載工具不能續傳,原因正在查詢,歡迎討論~
*******************************************
@ 2010-11-30 21:29
不知道為何就是不支援ie核心的瀏覽器自帶的下載工具進行斷點續傳。。。自行模擬ie核心瀏覽器客戶端,發完全一樣的http header到servlet,明顯能順利斷點續傳,初步估計是瀏覽器處理問題,最值得懷疑的是“Connection:close”,用ff,chrome,opera都沒有問題,都是keep-alive,求高手回答~!
轉自:http://www.shaojiahao.org/tag/%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0