`
vvggsky
  • 浏览: 65501 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

一个简单的多线程、断点下载Java程序

    博客分类:
  • J2SE
阅读更多
//这个是任务Bean   
  
public class Task {   
  
  
  
  private String downURL;   
  
  
  
  private String saveFile;   
  
     
  
  private int bufferSize = 64 * 1024;   
  
     
  
  private int workerCount;   
  
     
  
  private int sectionCount;   
  
     
  
  private long contentLength;   
  
     
  
  private long[] sectionsOffset;   
  
     
  
  public static final int HEAD_SIZE = 4096;   
  
     
  
  //读下载描述文件内容   
  
  public synchronized void read(RandomAccessFile file) throws IOException {   
  
    byte[] temp = new byte[HEAD_SIZE];   
  
    file.seek(0);   
  
    int readed = file.read(temp);   
  
    if (readed != temp.length) {   
  
      throw new RuntimeException();   
  
    }   
  
    ByteArrayInputStream bais = new ByteArrayInputStream(temp);   
  
    DataInputStream dis = new DataInputStream(bais);   
  
    downURL = dis.readUTF();   
  
    saveFile = dis.readUTF();   
  
    sectionCount = dis.readInt();   
  
    contentLength = dis.readLong();   
  
       
  
    sectionsOffset = new long[sectionCount];   
  
    for (int i = 0; i < sectionCount; i++) {   
  
      sectionsOffset[i] = file.readLong();   
  
    }   
  
  }   
  
     
  
  //创建下载描述文件内容   
  
  public synchronized void create(RandomAccessFile file) throws IOException {   
  
    if (sectionCount != sectionsOffset.length) {   
  
      throw new RuntimeException();   
  
    }   
  
    long len = HEAD_SIZE + 8 * sectionCount;   
  
    file.setLength(len);   
  
    ByteArrayOutputStream baos = new ByteArrayOutputStream();   
  
    DataOutputStream dos = new DataOutputStream(baos);   
  
    dos.writeUTF(downURL);   
  
    dos.writeUTF(saveFile);   
  
    dos.writeInt(sectionCount);   
  
    dos.writeLong(contentLength);   
  
    byte[] src = baos.toByteArray();   
  
    byte[] temp = new byte[HEAD_SIZE];   
  
    System.arraycopy(src, 0, temp, 0, src.length);   
  
    file.seek(0);   
  
    file.write(temp);   
  
    writeOffset(file);   
  
  }   
  
     
  
  //更新下载的过程   
  
  public synchronized void writeOffset(RandomAccessFile file) throws IOException {   
  
    if (sectionCount != sectionsOffset.length) {   
  
      throw new RuntimeException();   
  
    }   
  
    file.seek(HEAD_SIZE);   
  
    for (int i = 0; i < sectionsOffset.length; i++) {   
  
      file.writeLong(sectionsOffset[i]);   
  
    }   
  
  }   
  
  (下面是Getter、Setter)   
  
}   
  
  
  
//这个是下载主程序   
  
  
  
public class TaskAssign {   
  
  
  
  public void work(Task task) throws IOException {   
  
    File file = new File(task.getSaveFile());   
  
    if (file.exists()) {   
  
      return;   
  
    }   
  
    //这个是记录是否下载成功。我这里也没有增加失败回复、重试之类的工作。   
  
    final AtomicBoolean success = new AtomicBoolean(true);   
  
    //任务描述文件   
  
    File taskFile = new File(task.getSaveFile() + ".r_task");   
  
    //真正下载的文件   
  
    File saveFile = new File(task.getSaveFile() + ".r_save");   
  
    boolean taskFileExist = taskFile.exists();   
  
    RandomAccessFile taskRandomFile = null;   
  
    RandomAccessFile downRandomFile = null;   
  
    try {   
  
      taskRandomFile = new RandomAccessFile(taskFile, "rw");   
  
      downRandomFile = new RandomAccessFile(saveFile, "rw");   
  
      long rtnLen = getContentLength(task.getDownURL());   
  
      if (!taskFileExist) {   
  
        //如果文件不存在,就初始化任务文件和下载文件   
  
        task.setContentLength(rtnLen);   
  
        initTaskFile(taskRandomFile, task);   
  
        downRandomFile.setLength(rtnLen);   
  
      } else {   
  
        //任务文件存在就读取任务文件   
  
        task.read(taskRandomFile);   
  
        if (task.getContentLength() != rtnLen) {   
  
          throw new RuntimeException();   
  
        }   
  
      }   
  
      int secCount = task.getSectionCount();   
  
      //分配线程去下载,这里用到线程池   
  
      ExecutorService es = Executors.newFixedThreadPool(task.getWorkerCount());   
  
      for (int i = 0; i < secCount; i++) {   
  
        final int j = i;   
  
        final Task t = task;   
  
        final RandomAccessFile f1 = taskRandomFile;   
  
        final RandomAccessFile f2 = downRandomFile;   
  
        es.execute(new Runnable() {   
  
          public void run() {   
  
            try {   
  
              down(f1, f2, t, j);   
  
            } catch (IOException e) {   
  
              success.set(false);   
  
              e.printStackTrace(System.out);   
  
            }   
  
          }   
  
        });   
  
      }   
  
      es.shutdown();   
  
      try {   
  
        es.awaitTermination(24 * 3600, TimeUnit.SECONDS);   
  
      } catch (InterruptedException e) {   
  
        e.printStackTrace();   
  
      }   
  
      taskRandomFile.close();   
  
      taskRandomFile = null;   
  
      downRandomFile.close();   
  
      downRandomFile = null;   
  
      //如果下载成功,去掉任务描述文件、帮下载文件改名   
  
      if (success.get()) {   
  
        taskFile.delete();   
  
        saveFile.renameTo(file);   
  
      }   
  
    } finally {   
  
      if (taskRandomFile != null) {   
  
        taskRandomFile.close();   
  
        taskRandomFile = null;   
  
      }   
  
      if (downRandomFile != null) {   
  
        downRandomFile.close();   
  
        downRandomFile = null;   
  
      }   
  
    }   
  
  }   
  
     
  
  public void down(RandomAccessFile taskRandomFile, RandomAccessFile downRandomFile, Task task, int sectionNo) throws IOException {   
  
    //这里我用HttpURLConnection下载,你也可以用HttpClient或者自己实现一个Http协议(不过貌似没有必要)   
  
    URL u = new URL(task.getDownURL());   
  
    HttpURLConnection conn = (HttpURLConnection) u.openConnection();   
  
    long start = task.getSectionsOffset()[sectionNo];   
  
    long end = -1;   
  
    //这里要注意一下,这里是计算当前块的长度   
  
    if (sectionNo < task.getSectionCount() - 1) {   
  
      long per = task.getContentLength() / task.getSectionCount();   
  
      end = per * (sectionNo + 1);   
  
    } else {   
  
      end = task.getContentLength();   
  
    }   
  
    if (start >= end) {   
  
      System.out.println("Section has finished before. " + sectionNo);   
  
      return;   
  
    }   
  
    String range = "bytes=" + start + "-" + (end - 1);   
  
    conn.setRequestProperty("Range", range);   
  
    conn.setRequestProperty("User-Agent", "Ray-Downer");   
  
    try {   
  
      conn.connect();   
  
      if (conn.getResponseCode() != 206) {   
  
        throw new RuntimeException();   
  
      }   
  
      if (conn.getContentLength() != (end - start)) {   
  
        throw new RuntimeException();   
  
      }   
  
      InputStream is = conn.getInputStream();   
  
      byte[] temp = new byte[task.getBufferSize()];   
  
      BufferedInputStream bis = new BufferedInputStream(is, temp.length);   
  
      int readed = 0;   
  
      while ((readed = bis.read(temp)) > 0) {   
  
        long offset = task.getSectionsOffset()[sectionNo];   
  
        synchronized (task) {   
  
          //下载之后顺便更新描述文件,你可能会发现这里效率比较低,在一个线程同步里进行两次文件操作。你可以自己实现一个缓冲写。   
  
          downRandomFile.seek(offset);   
  
          downRandomFile.write(temp, 0, readed);   
  
          offset += readed;   
  
          task.getSectionsOffset()[sectionNo] = offset;   
  
          task.writeOffset(taskRandomFile);   
  
        }   
  
      }   
  
    } finally {   
  
      conn.disconnect();   
  
    }   
  
    System.out.println("Section finished. " + sectionNo);   
  
  }   
  
     
  
  public void initTaskFile(RandomAccessFile taskRandomFile, Task task) throws IOException {   
  
    int secCount = task.getSectionCount();   
  
    long per = task.getContentLength() / secCount;   
  
    long[] sectionsOffset = new long[secCount];   
  
    for (int i = 0; i < secCount; i++) {   
  
      sectionsOffset[i] = per * i;   
  
    }   
  
    task.setSectionsOffset(sectionsOffset);   
  
    task.create(taskRandomFile);   
  
  }   
  
     
  
  public long getContentLength(String url) throws IOException {   
  
    URL u = new URL(url);   
  
    HttpURLConnection conn = (HttpURLConnection) u.openConnection();   
  
    try {   
  
      return conn.getContentLength();   
  
    } finally {   
  
      conn.disconnect();   
  
    }   
  
  }   
  
}   
  
  
  
//稍微测试一下。   
  
public class Main {   
  
     
  
  public static void main(String[] args) throws IOException {   
  
    test3();   
  
    System.out.println("\n\n===============\nFinished.");   
  
  }   
  
    
  
  public static void test1() throws IOException {   
  
    Task task = new Task();   
  
    task.setDownURL("http://61.152.235.21/qqfile/qq/2007iistable/QQ2007IIKB1.exe");   
  
    task.setSaveFile("H:/Test2.exe");   
  
    task.setSectionCount(200);   
  
    task.setWorkerCount(100);   
  
    task.setBufferSize(256 * 1024);   
  
    TaskAssign ta = new TaskAssign();   
  
    ta.work(task);   
  
  }   
  
     
  
  public static void test2() throws IOException {   
  
    Task task = new Task();   
  
    task.setDownURL("http://student1.scut.edu.cn:8880/manage/news/data/1208421861893.xls");   
  
    task.setSaveFile("H:/Test3.xls");   
  
    task.setSectionCount(5);   
  
    task.setWorkerCount(1);   
  
    task.setBufferSize(128 * 1024);   
  
    TaskAssign ta = new TaskAssign();   
  
    ta.work(task);   
  
  }   
  
     
  
  public static void test3() throws IOException {   
  
    Task task = new Task();   
  
    task.setDownURL("http://go.microsoft.com/fwlink/?linkid=57034");   
  
    task.setSaveFile("H:/vc2005express.iso");   
  
    task.setSectionCount(500);   
  
    task.setWorkerCount(200);   
  
    task.setBufferSize(128 * 1024);   
  
    TaskAssign ta = new TaskAssign();   
  
    ta.work(task);   
  
  }   
  
     
  
  public static void test4() throws IOException {   
  
    Task task = new Task();   
  
    task.setDownURL("http://down.sandai.net/Thunder5.7.9.472.exe");   
  
    task.setSaveFile("H:/Thunder.exe");   
  
    task.setSectionCount(30);   
  
    task.setWorkerCount(30);   
  
    task.setBufferSize(128 * 1024);   
  
    TaskAssign ta = new TaskAssign();   
  
    ta.work(task);   
  
  }   
  
}  

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics