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. }