直接看代碼吧,廢話一點不多說。
功能介紹:
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. }