`
java2000.net
  • 浏览: 651311 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

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

    博客分类:
  • JAVA
阅读更多

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

功能介绍:

1 多线程HTTP下载

2 支持断点续传

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

4 提供防盗链的破解

 

package net.java2000.tools;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

/**
 * HTTP的多线程下载工具。
 * 
 * @author 赵学庆 www.java2000.net
 */
public class HTTPDownloader extends Thread {
  // 要下载的页面
  private String page;

  // 保存的路径
  private String savePath;

  // 线程数
  private int threadNumber = 2;

  // 来源地址
  private String referer;

  private String cookie;

  int threadPointer = 0;

  private Map<Integer, HTTPDownloaderThread> threadPool = new HashMap<Integer, HTTPDownloaderThread>(); // 线程迟

  // 最小的块尺寸。如果文件尺寸除以线程数小于这个,则会减少线程数。
  private int MIN_BLOCK = 10 * 1024;

  public static void main(String[] args) throws Exception {
    HTTPDownloader d = new HTTPDownloader("http://www.xxxxx.com/a.rar", null, "d://a.rar", 10, null);
    d.down();
  }

  public void run() {
    try {
      down();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 下载操作
   * 
   * @throws Exception
   */
  public void down() throws Exception {
    URL url = new URL(page); // 创建URL
    URLConnection con = url.openConnection(); // 建立连接
    con.setRequestProperty("Referer", referer == null ? page : referer);
    con.setRequestProperty("UserAgent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; flashget)");
    int contentLen = con.getContentLength(); // 获得资源长度
    if ((contentLen / MIN_BLOCK + 1) < threadNumber) {
      threadNumber = contentLen / MIN_BLOCK + 1; // 调整下载线程数
    }
    if (threadNumber > 10) {
      threadNumber = 10;
    }
    int begin = 0;
    int step = contentLen / threadNumber + 1;
    int end = 0;
    HTTPDownloaderThread thread;
    for (threadPointer = 0; threadPointer < threadNumber; threadPointer++) {
      end += step;
      if (end > contentLen) {
        end = contentLen;
      }
      thread = new HTTPDownloaderThread(this, threadPointer, begin, end);
      threadPool.put(threadPointer, thread);
      thread.start();
      begin = end;
    }
  }

  /**
   * 一个线程完活了。
   * 
   * @param id 完活的线程id
   */
  public synchronized void finished(int id) {
    threadNumber--;
    threadPool.remove(id);
    if (threadNumber <= 0) {
      System.out.println("FINISHED:" + savePath);
      File f1 = new File(savePath + ".tmp");
      File f2 = new File(savePath);
      // 如果目标文件已经存在,则尝试删除它
      // 最多尝试3次,间隔1秒钟。
      int times = 3;
      while (f2.exists() && times > 0) {
        if (f2.delete()) {
          break;
        }
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        times--;
      }
      if (!f2.exists()) {
        if (!f1.renameTo(f2)) {
          System.out.println("改名失败!");
        }
      } else {
        System.out.println("目标文件存在,且无法删除,无法改名");
      }
    } else {
      int size = 0;
      HTTPDownloaderThread o = null;
      // 尝试查找一个可以分担的线程
      for (HTTPDownloaderThread thread : threadPool.values()) {
        if (thread.endPos - thread.curPos > size) {
          size = thread.endPos - thread.curPos;
          o = thread;
        }
      }
      if (size > MIN_BLOCK * 2) {
        if (o.isAlive()) {
          int endPos = o.endPos;
          int beginPos = o.endPos - ((o.endPos - o.curPos) / 2);
          o.endPos = beginPos;
          threadNumber++;
          threadPointer++;
          HTTPDownloaderThread thread = new HTTPDownloaderThread(this, threadPointer, beginPos, endPos);
          threadPool.put(threadPointer, thread);
          System.out.println("A Help Thread for " + o.id + " is started with:" + threadPointer);
          thread.start();
        }
      }
    }
  }

  public HTTPDownloader() {
  }

  /**
   * 下载
   * 
   * @param page 被下载的页面
   * @param savePath 保存的路径
   */
  public HTTPDownloader(String page, String savePath) {
    this(page, savePath, 10);
  }

  /**
   * 下载
   * 
   * @param page 被下载的页面
   * @param savePath 保存的路径
   * @param threadNumber 线程数
   */
  public HTTPDownloader(String page, String savePath, int threadNumber) {
    this(page, page, savePath, 10, null);
  }

  /**
   * 下载
   * 
   * @param page 被下载的页面
   * @param savePath 保存的路径
   * @param threadNumber 线程数
   * @param referer 来源
   */
  public HTTPDownloader(String page, String referer, String savePath, int threadNumber, String cookie) {
    this.page = page;
    this.savePath = savePath;
    this.threadNumber = threadNumber;
    this.referer = referer;
  }

  public String getPage() {
    return page;
  }

  public void setPage(String page) {
    this.page = page;
  }

  public String getSavePath() {
    return savePath;
  }

  public void setSavePath(String savePath) {
    this.savePath = savePath;
  }

  public int getThreadNumber() {
    return threadNumber;
  }

  public void setThreadNumber(int threadNumber) {
    this.threadNumber = threadNumber;
  }

  public String getReferer() {
    return referer;
  }

  public void setReferer(String referer) {
    this.referer = referer;
  }

  public String getCookie() {
    return cookie;
  }

  public void setCookie(String cookie) {
    this.cookie = cookie;
  }
}

/**
 * 下载线程
 * 
 * @author 赵学庆 www.java2000.net
 */
class HTTPDownloaderThread extends Thread {
  HTTPDownloader manager;

  int startPos;

  int endPos;

  int id;

  int curPos;

  int BUFFER_SIZE = 40960;

  int readByte = 0;

  HTTPDownloaderThread(HTTPDownloader manager, int id, int startPos, int endPos) {
    this.id = id;
    this.manager = manager;
    this.startPos = startPos;
    this.endPos = endPos;
  }

  public void run() {
    System.out.println("线程" + id + "启动," + startPos + "-" + endPos);
    // 创建一个buff
    BufferedInputStream bis = null;
    RandomAccessFile fos = null;
    // 缓冲区大小
    byte[] buf = new byte[BUFFER_SIZE];
    boolean timeout = false;
    Socket socket = null;
    try {
      curPos = startPos;
      File file = new File(manager.getSavePath() + ".tmp");
      // 创建RandomAccessFile
      fos = new RandomAccessFile(file, "rw");
      // 从startPos开始
      fos.seek(startPos);
      int index = manager.getPage().indexOf("/", 8);
      String host = manager.getPage().substring(7, index);
      // System.out.println(host);
      socket = new Socket(host, 80);
      socket.setSoTimeout(30000);
      // 写入数据
      BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
      StringBuilder b = new StringBuilder();
      b.append("GET " + manager.getPage().substring(index) + " HTTP/1.1\r\n");
      b.append("Host: " + host + "\r\n");
      b.append("Referer: " + (manager.getReferer() == null ? manager.getPage() : manager.getReferer()) + "\r\n");
      b.append("UserAgent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; flashget; \r\n");
      b.append("Range: bytes=" + startPos + "-" + endPos + "\r\n");
      b.append("\r\n");
      // System.out.println(b.toString());
      wr.write(b.toString());
      wr.flush();
      // 下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
      // 如果超过endPos就代表该线程已经执行完毕
      bis = new BufferedInputStream(socket.getInputStream());
      // 读取直到换行
      int ch;
      boolean foundBR = false;
      while (true) {
        ch = bis.read();
        if (ch == 0xD) {
          ch = bis.read();
          if (ch == 0xA) {
            if (foundBR) {
              break;
            }
            foundBR = true;
          } else {
            foundBR = false;
          }
        } else {
          foundBR = false;
        }
      }
      int len = -1;
      while (curPos < endPos) {
        // System.out.println(id + "=" + (endPos - curPos));
        len = bis.read(buf, 0, BUFFER_SIZE);
        if (len == -1) {
          break;
        }
        fos.write(buf, 0, len);
        // System.out.println(id + "=Write OK!");
        curPos = curPos + len;
        if (curPos > endPos) {
          // 获取正确读取的字节数
          readByte += len - (curPos - endPos) + 1;
        } else {
          readByte += len;
        }
      }
      System.out.println("线程" + id + "已经下载完毕:" + readByte);
    } catch (Exception ex) {
      timeout = true;
    } finally {
      if (bis != null) {
        try {
          bis.close();
        } catch (Exception e) {
          System.out.println("关闭文件失败(1)!");
        }
      }
      if (fos != null) {
        try {
          fos.close();
        } catch (Exception e) {
          System.out.println("关闭文件失败(2)!");
        }
      }
      if (socket != null) {
        try {
          socket.close();
        } catch (Exception e) {
          System.out.println("关闭链接失败!");
        }
      }
    }
    if (timeout) {
      System.out.println(id + " timeout, restart...");
      new HTTPDownloaderThread(manager, id, curPos, endPos).start();
    } else {
      manager.finished(id);
    }
  }
}
 
4
0
分享到:
评论
3 楼 luofuyong 2012-05-08  
支持web应用的下载吗
2 楼 ice.k 2009-06-19  
有用。。
1 楼 Saito 2008-09-13  
紫竹大哥也来这边发展了..


支持一个先..

相关推荐

    java ftp 多线程 断点续传等知识

    而"多线程"和"断点"这两个文件名可能是指相关示例代码或文档,可以进一步帮助你理解和实践Java FTP的多线程下载和断点续传。 在实际应用中,还需要考虑其他因素,如错误处理、网络状况的监控、文件完整性检查等。...

    java断点续传,刚刚整理出来多线程处理

    然而,实现多线程断点续传需要解决几个问题: 1. **同步管理**:多个线程可能会同时访问同一个文件的部分,因此需要使用`synchronized`关键字或`Lock`对象来确保并发安全。 2. **断点信息共享**:每个线程需要知道...

    android多线程断点续传下载

    在Android开发中,多线程断点续传下载是一项重要的技术,它允许用户在网络连接不稳定或设备关闭后,从上次中断的地方继续下载大文件,提高用户体验。下面将详细讲解这个主题。 首先,理解“多线程下载”。在单线程...

    多线程断点续传

    多线程断点续传是一种高效且实用的文件下载技术,尤其在处理大文件或网络不稳定的情况下,能够显著提升用户体验。下面将详细解释这一技术的关键概念、实现原理以及相关知识点。 1. **多线程**:多线程是指程序中...

    android断点续传_多线程下载demo

    在Android开发中,断点续传和多线程下载是提高用户下载体验的重要技术。本文将深入探讨如何在Android客户端实现这些功能,并结合服务器端的配合来完成整个流程。 首先,断点续传(Resumable Download)允许用户在...

    单线程断点续传

    "单线程断点续传"这个实例主要关注的是如何在一个线程中实现这个功能,避免了多线程可能带来的复杂性和资源消耗。 首先,我们需要理解服务(Service)在Android开发中的角色。在Android中,Service是一种后台运行的...

    android多线程断点续传

    而多线程断点续传技术则能够很好地解决这一问题,它不仅能够提高下载速度,还能实现断点续传功能,即在网络中断后能够从上次断开的地方继续下载。 #### 二、关键技术点解析 ##### 1. 多线程下载原理 - **分块下载*...

    支持多线程断点续传的文件传输(FTP)程序

    "支持多线程断点续传的文件传输(FTP)程序"是一个高效且实用的工具,它结合了多线程技术和断点续传功能,使得大文件传输更加稳定和高效。 首先,我们来理解"多线程"的概念。在计算机编程中,多线程是指一个程序中...

    多线程断点续传 下载软件 研究 开发 毕业设计

    多线程断点续传下载软件是现代网络下载技术中的一个重要组成部分,尤其在处理大文件或者网络环境不稳定的情况下,它的优势显著。这类软件允许用户在下载过程中暂停、恢复,甚至在多个线程同时进行,提高了下载速度和...

    ftp协议实现多线程断点续传

    在VC++环境中,实现FTP协议的多线程断点续传,通常需要以下步骤: 1. **理解FTP协议**:首先,你需要了解FTP的基本工作原理,包括命令和响应机制,如“USER”、“PASS”用于登录,“PASV”或“PORT”用于设置数据...

    delphi多线程HTTP断点续传下载

    本主题聚焦于"delphi多线程HTTP断点续传下载",这是一种高效且用户友好的文件下载策略,它允许用户在中断网络连接后从上次停止的地方继续下载,无需重新开始。 首先,我们需要理解多线程的概念。在单线程程序中,...

    关于Android多线程断点续传下载

    在Android开发中,多线程断点续传下载是一项重要的技术,它允许应用程序在用户暂停或因网络问题中断下载后,从上次停止的地方继续下载,提高用户体验并节省资源。下面将详细阐述这一技术的实现原理、关键步骤以及...

    Java多线程与线程安全实践-基于Http协议的断点续传.zip

    在大型系统和网络应用中,如基于HTTP协议的断点续传功能,多线程技术尤为关键。本实践项目将深入探讨Java如何实现多线程以及在并发环境下确保线程安全的方法。 首先,多线程是指一个程序中可以同时执行多个独立的...

    itcast传智播客android多线程断点续传下载代码

    本项目"itcast传智播客android多线程断点续传下载代码"提供了完整的实现方案,允许用户在中断下载后从上次停止的位置继续,提高了用户体验。以下是关于这个项目的关键知识点: 1. **多线程下载**: - 多线程下载是...

    基于java的多线程断点续传程序源代码

    在这个“基于Java的多线程断点续传程序”中,我们主要关注的是如何利用Java的多线程特性和网络编程能力来实现文件的断点续传功能。这个程序可能是为了满足在不完整的下载过程中能够继续从上次中断的位置开始下载的...

    计算机软件-编程源码-GetFast(多线程断点续传下载示例).zip

    《GetFast:多线程断点续传下载技术解析》 在计算机编程领域,源码软件是程序员进行学习、研究和开发的重要资源。本篇将深入探讨“GetFast”这一多线程断点续传下载示例,揭示其背后的原理和技术实现。 一、多线程...

    FTP多线程上传下载、断点续传、分段下载--田景吉之C#版本

    本文将深入探讨在C#环境中实现FTP多线程上传下载、断点续传以及分段下载的技术细节。 首先,让我们理解多线程的概念。在单线程环境下,FTP上传和下载可能受限于网络带宽和处理器的单一执行能力。通过多线程,我们...

Global site tag (gtag.js) - Google Analytics