HTTP多线程断点续传下载的尝试2010-12-16java2000直接看代码吧,废话一点不多说。功能介绍: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.net16. */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 Exception56. */57.public void down() throws Exception {58.URL url = new URL(page); // 创建URL59.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 完活的线程id89. */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.net228. */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.// 创建一个buff254.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.// 创建RandomAccessFile264.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
");276.b.append("Host: " + host + "
");277.b.append("Referer: " + (manager.getReferer() == null ? manager.getPage() : manager.getReferer()) + "
");278.b.append("UserAgent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; flashget;
");279.b.append("Range: bytes=" + startPos + "-" + endPos + "
");280.b.append("
");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.}