本文實例為大家分享了java斷點續傳下載的代碼,供大家參考,具體內容如下
1. Java代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
//實現文件下載功能 public String downloadFile(){ File dir = new File(filepath); //獲取文件路勁 if (!dir.exists()) { System.out.println( "文件路徑錯誤" ); log.debug( "文件路徑錯誤" ); return "failed" ; // 判斷文件或文件夾是否存在 } File downloadFile = new File(dir, filename); //在指定目錄下查找文件 if (!downloadFile.isFile()){ System.out.println( "文件不存在" ); log.debug( "文件不存在" ); return "failed" ; // 判斷文件或文件夾是否存在 } try { downloadFileRanges(downloadFile); } catch (ClientAbortException e){ System.out.println( "連接被終止" ); log.debug( "連接被終止" ); } catch (IOException e) { e.printStackTrace(); } return null ; } private void downloadFileRanges(File downloadFile) throws IOException { // 要下載的文件大小 long fileLength = downloadFile.length(); // 已下載的文件大小 long pastLength = 0 ; // 是否快車下載,否則為迅雷或其他 boolean isFlashGet = true ; // 用于記錄需要下載的結束字節數(迅雷或其他下載) long lenEnd = 0 ; // 用于記錄客戶端要求下載的數據范圍字串 String rangeBytes = request.getHeader( "Range" ); //用于隨機讀取寫入文件 RandomAccessFile raf = null ; OutputStream os = null ; OutputStream outPut = null ; byte b[] = new byte [ 1024 ]; // 如果客戶端下載請求中包含了范圍 if ( null != rangeBytes) { // 返回碼 206 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); rangeBytes = request.getHeader( "Range" ).replaceAll( "bytes=" , "" ); // 判斷 Range 字串模式 if (rangeBytes.indexOf( '-' ) == rangeBytes.length() - 1 ) { // 無結束字節數,為快車 isFlashGet = true ; rangeBytes = rangeBytes.substring( 0 , rangeBytes.indexOf( '-' )); pastLength = Long.parseLong(rangeBytes.trim()); } else { // 迅雷下載 isFlashGet = false ; String startBytes = rangeBytes.substring( 0 , rangeBytes.indexOf( '-' )); String endBytes = rangeBytes.substring( rangeBytes.indexOf( '-' ) + 1 , rangeBytes.length()); // 已下載文件段 pastLength = Long.parseLong(startBytes.trim()); // 還需下載的文件字節數(從已下載文件段開始) lenEnd = Long.parseLong(endBytes); } } // 通知客戶端允許斷點續傳,響應格式為:Accept-Ranges: bytes response.setHeader( "Accept-Ranges" , "bytes" ); // response.reset(); // 如果為第一次下載,則狀態默認為 200,響應格式為: HTTP/1.1 200 ok if ( 0 != pastLength) { // 內容范圍字串 String contentRange = "" ; // 響應格式 // Content-Range: bytes [文件塊的開始字節]-[文件的總大小 - 1]||[文件的總大小] if (isFlashGet) { contentRange = new StringBuffer( "bytes" ) .append( new Long(pastLength).toString()).append( "-" ) .append( new Long(fileLength - 1 ).toString()) .append( "/" ).append( new Long(fileLength).toString()) .toString(); } else { contentRange = new StringBuffer(rangeBytes).append( "/" ) .append( new Long(fileLength).toString()).toString(); } response.setHeader( "Content-Range" , contentRange); } String fileName = getDownloadChineseFileName(filename); response.setHeader( "Content-Disposition" , "attachment;filename=" + fileName + "" ); // 響應的格式是: response.setContentType( "application/octet-stream" ); response.addHeader( "Content-Length" , String.valueOf(fileLength)); try { os = response.getOutputStream(); outPut = new BufferedOutputStream(os); raf = new RandomAccessFile(downloadFile, "r" ); // 跳過已下載字節 raf.seek(pastLength); if (isFlashGet) { // 快車等 int n = 0 ; while ((n = raf.read(b, 0 , 1024 )) != - 1 ) { outPut.write(b, 0 , n); } } else { // 迅雷等 while (raf.getFilePointer() < lenEnd) { outPut.write(raf.read()); } } outPut.flush(); } catch (IOException e) { /** * 在寫數據的時候 對于 ClientAbortException 之類的異常 * 是因為客戶端取消了下載,而服務器端繼續向瀏覽器寫入數據時, 拋出這個異常,這個是正常的。 尤其是對于迅雷這種吸血的客戶端軟件。 * 明明已經有一個線程在讀取 bytes=1275856879-1275877358, * 如果短時間內沒有讀取完畢,迅雷會再啟第二個、第三個。。。線程來讀取相同的字節段, 直到有一個線程讀取完畢,迅雷會 KILL * 掉其他正在下載同一字節段的線程, 強行中止字節讀出,造成服務器拋 ClientAbortException。 * 所以,我們忽略這種異常 */ } finally { if (outPut != null ) { outPut.close(); } if (raf != null ) { raf.close(); } } } private String getDownloadChineseFileName(String paramName) { String downloadChineseFileName = "" ; try { downloadChineseFileName = new String(paramName.getBytes( "GBK" ), "ISO8859-1" ); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return downloadChineseFileName; } public String getFilepath() { return filepath; } public void setFilepath(String filepath) { this .filepath = filepath; } public String getFilename() { return filename; } public void setFilename(String filename) { this .filename = filename; } public HttpServletRequest getRequest() { return request; } public HttpServletResponse getResponse() { return response; } |
2. struts部分
<result name="failed" type="redirectAction">showDownloadFileNameList</result>
</action>
3. jsp部分