HTTP多线程断点续传下载的实验
直接看代码吧,空话一点不多说。
成果先容:
1 多线程HTTP下载
2 支持断点续传
3 姑且文件下载,乐成后更名
4 提供防盗链的破解
Java代码
1.import java.io.BufferedInputStream;
2.import java.io.BufferedWriter;
3.import java.io.File;
4.import java.io.OutputStreamWriter;
5.import java.io.RandomAccessFile;
6.import java.net.Socket;
7.import java.net.URL;
8.import java.net.URLConnection;
9.import java.util.HashMap;
10.import java.util.Map;
11.
12./**
13. * HTTP的多线程下载东西。
14. *
15. * @author 赵学庆 www.java2000.net
16. */
17.public class HTTPDownloader extends Thread {
18. // 要下载的页面
19. private String page;
20.
21. // 生存的路径
22. private String savePath;
23.
24. // 线程数
25. private int threadNumber = 2;
26.
27. // 来历地点
28. private String referer;
29.
30. private String cookie;
31.
32. int threadPointer = 0;
33.
34. private Map<Integer, HTTPDownloaderThread> threadPool = new HashMap<Integer, HTTPDownloaderThread>(); // 线程迟
35.
36. // 最小的块尺寸。假如文件尺寸除以线程数小于这个,则会淘汰线程数。 37. private int MIN_BLOCK = 10 * 1024;
38.
39. public static void main(String[] args) throws Exception {
40. HTTPDownloader d = new HTTPDownloader("http://www.xxxxx.com/a.rar", null, "d://a.rar", 10, null);
41. d.down();
42. }
43.
44. public void run() {
45. try {
46. down();
47. } catch (Exception e) {
48. e.printStackTrace();
49. }
50. }
51.
52. /**
53. * 下载操纵
54. *
55. * @throws Exception
56. */
57. public void down() throws Exception {
58. URL url = new URL(page); // 建设URL
59. URLConnection con = url.openConnection(); // 成立毗连
60. con.setRequestProperty("Referer", referer == null ? page : referer);
61. con.setRequestProperty("UserAgent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; flashget)");
62. int contentLen = con.getContentLength(); // 得到资源长度
63. if ((contentLen / MIN_BLOCK + 1) < threadNumber) {
64. threadNumber = contentLen / MIN_BLOCK + 1; // 调解下载线程数 65. }
66. if (threadNumber > 10) {
67. threadNumber = 10;
68. }
69. int begin = 0;
70. int step = contentLen / threadNumber + 1;
71. int end = 0;
72. HTTPDownloaderThread thread;
73. for (threadPointer = 0; threadPointer < threadNumber; threadPointer++) {
74. end += step;
75. if (end > contentLen) {
76. end = contentLen;
77. }
78. thread = new HTTPDownloaderThread(this, threadPointer, begin, end);
79. threadPool.put(threadPointer, thread);
80. thread.start();
81. begin = end;
82. }
83. }
84.
85. /**
86. * 一个线程完活了。
87. *
88. * @param id 完活的线程id
89. */
90. public synchronized void finished(int id) {
91. threadNumber--;
92. threadPool.remove(id);
93. if (threadNumber <= 0) {
94. System.out.println("FINISHED:" + savePath);
95. File f1 = new File(savePath + ".tmp");
96. File f2 = new File(savePath);
97. // 假如方针文件已经存在,则实验删除它
98. // 最多实验3次,隔断1秒钟。
99. int times = 3;
100. while (f2.exists() && times > 0) {
101. if (f2.delete()) {
102. break;
103. }
104. try {
105. Thread.sleep(1000);
106. } catch (InterruptedException e) {
107. e.printStackTrace();
108. }
109. times--;
110. }
111. if (!f2.exists()) {
112. if (!f1.renameTo(f2)) {
113. System.out.println("更名失败!");
114. }
115. } else {
116. System.out.println("方针文件存在,且无法删除,无法更名");
117. }
118. } else {
119. int size = 0;
120. HTTPDownloaderThread o = null;
121. // 实验查找一个可以分管的线程
122. for (HTTPDownloaderThread thread : threadPool.values()) {
123. if (thread.endPos - thread.curPos > size) {
124. size = thread.endPos - thread.curPos;
125. o = thread;
126. }
127. }
128. if (size > MIN_BLOCK * 2) {
129. if (o.isAlive()) {
130. int endPos = o.endPos;
131. int beginPos = o.endPos - ((o.endPos - o.curPos) / 2);
132. o.endPos = beginPos;
133. threadNumber++;
134. threadPointer++;
135. HTTPDownloaderThread thread = new HTTPDownloaderThread(this, threadPointer, beginPos, endPos);
136. threadPool.put(threadPointer, thread);
137. System.out.println("A Help Thread for " + o.id + " is started with:" + threadPointer);
138. thread.start();
139. }
140. }
141. }
142. }
143.
144. public HTTPDownloader() {
145. }
146.
147. /**
148. * 下载
149. *
150. * @param page 被下载的页面
151. * @param savePath 生存的路径
152. */
153. public HTTPDownloader(String page, String savePath) {
154. this(page, savePath, 10);
155. }
156.
157. /**
158. * 下载
159. *
160. * @param page 被下载的页面
161. * @param savePath 生存的路径
162. * @param threadNumber 线程数
163. */
164. public HTTPDownloader(String page, String savePath, int threadNumber) {
165. this(page, page, savePath, 10, null);
166. }
167.
168. /**
169. * 下载
170. *
171. * @param page 被下载的页面
172. * @param savePath 生存的路径
173. * @param threadNumber 线程数
174. * @param referer 来历
175. */
176. public HTTPDownloader(String page, String referer, String savePath, int threadNumber, String cookie) {
177. this.page = page;
178. this.savePath = savePath;
179. this.threadNumber = threadNumber;
180. this.referer = referer;
181. }
182.
183. public String getPage() {
184. return page;
185. }
186.
187. public void setPage(String page) {
188. this.page = page;
189. }
190.
191. public String getSavePath() {
192. return savePath;
193. }
194.
195. public void setSavePath(String savePath) {
196. this.savePath = savePath;
197. }
198.
199. public int getThreadNumber() {
200. return threadNumber;
201. }
202.
203. public void setThreadNumber(int threadNumber) {
204. this.threadNumber = threadNumber;
205. }
206.
207. public String getReferer() {
208. return referer;
209. }
210.
211. public void setReferer(String referer) {
212. this.referer = referer;
213. }
214.
215. public String getCookie() {
216. return cookie;
217. }
218.
219. public void setCookie(String cookie) {
220. this.cookie = cookie;
221. }
222.}
223.
224./**
225. * 下载线程
226. *
227. * @author 赵学庆 www.java2000.net
228. */
229.class HTTPDownloaderThread extends Thread {
230. HTTPDownloader manager;
231.
232. int startPos;
233.
234. int endPos;
235.
236. int id;
237.
238. int curPos;
239.
240. int BUFFER_SIZE = 40960;
241.
242. int readByte = 0;
243.
244. HTTPDownloaderThread(HTTPDownloader manager, int id, int startPos, int endPos) {
245. this.id = id;
246. this.manager = manager;
247. this.startPos = startPos;
248. this.endPos = endPos;
249. }
250.
251. public void run() {
252. System.out.println("线程" + id + "启动," + startPos + "-" + endPos);
253. // 建设一个buff
254. BufferedInputStream bis = null;
255. RandomAccessFile fos = null;
256. // 缓冲区巨细
257. byte[] buf = new byte[BUFFER_SIZE];
258. boolean timeout = false;
259. Socket socket = null;
260. try {
261. curPos = startPos;
262. File file = new File(manager.getSavePath() + ".tmp");
263. // 建设RandomAccessFile
264. fos = new RandomAccessFile(file, "rw");
265. // 从startPos开始
266. fos.seek(startPos);
267. int index = manager.getPage().indexOf("/", 8);
268. String host = manager.getPage().substring(7, index);
269. // System.out.println(host);
270. socket = new Socket(host, 80);
271. socket.setSoTimeout(30000);
272. // 写入数据
273. BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
274. StringBuilder b = new StringBuilder();
275. b.append("GET " + manager.getPage().substring(index) + " HTTP/1.1\r\n");
276. b.append("Host: " + host + "\r\n");
277. b.append("Referer: " + (manager.getReferer() == null ? manager.getPage() : manager.getReferer()) + "\r\n");
278. b.append("UserAgent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; flashget; \r\n");
279. b.append("Range: bytes=" + startPos + "-" + endPos + "\r\n");
280. b.append("\r\n");
281. // System.out.println(b.toString());
282. wr.write(b.toString());
283. wr.flush();
284. // 下面一段向按照文件写入数据,curPos为当前写入的未知,这里会判定是否小于endPos,
285. // 假如高出endPos就代表该线程已经执行完毕
286. bis = new BufferedInputStream(socket.getInputStream());
287. // 读取直到换行
288. int ch;
289. boolean foundBR = false;
290. while (true) {
291. ch = bis.read();
292. if (ch == 0xD) {
293. ch = bis.read();
294. if (ch == 0xA) {
295. if (foundBR) {
296. break;
297. }
298. foundBR = true;
299. } else {
300. foundBR = false;
301. }
302. } else {
303. foundBR = false;
304. }
305. }
306. int len = -1;
307. while (curPos < endPos) {
308. // System.out.println(id + "=" + (endPos - curPos));
309. len = bis.read(buf, 0, BUFFER_SIZE);
310. if (len == -1) {
311. break;
312. }
313. fos.write(buf, 0, len);
314. // System.out.println(id + "=Write OK!");
315. curPos = curPos + len;
316. if (curPos > endPos) {
317. // 获取正确读取的字节数
318. readByte += len - (curPos - endPos) + 1;
319. } else {
320. readByte += len;
321. }
322. }
323. System.out.println("线程" + id + "已经下载完毕:" + readByte);
324. } catch (Exception ex) {
325. timeout = true;
326. } finally {
327. if (bis != null) {
328. try {
329. bis.close();
330. } catch (Exception e) {
331. System.out.println("封锁文件失败(1)!");
332. }
333. }
334. if (fos != null) {
335. try {
336. fos.close();
337. } catch (Exception e) {
338. System.out.println("封锁文件失败(2)!");
339. }
340. }
341. if (socket != null) {
342. try {
343. socket.close();
344. } catch (Exception e) {
345. System.out.println("封锁链接失败!");
346. }
347. }
348. }
349. if (timeout) {
350. System.out.println(id + " timeout, restart...");
351. new HTTPDownloaderThread(manager, id, curPos, endPos).start();
352. } else {
353. manager.finished(id);
354. }
355. }
356. }