HTTP多线程断点续传下载的尝试

来源:互联网 发布:什么是淘宝返利 编辑:程序博客网 时间:2024/05/22 16:48
转载自:http://java2000-net.javaeye.com/blog/241376
作者:赵学庆
--------------------------------------------

直接看代码吧,废话一点不多说。

功能介绍:

1 多线程HTTP下载

2 支持断点续传

3 临时文件下载,成功后改名

4 提供防盗链的破解

  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"10null);  
  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, 10null);  
  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.     }  


原创粉丝点击