Java多线程下载文件

来源:互联网 发布:java运维和实施面试题 编辑:程序博客网 时间:2024/05/16 08:07

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

 

/**

 *@author EX-QINCIDONG001多线程下载文件。 思想:开启5个线程,每个线程下载文件大小的1/5,<br>

 *        每个线程生成的文件以文件名_编号作为文件名。<br>

 *        每个线程结束后都检查自己是否是最后一个线程。<br>

 *         如果是最后一个线程,就启动合并文件的线程<br>

 *        用RandomAccessFile类,有追加文件的方法。<br>

 *        扫描所有的文件,合并为一个文件。

 *

 *        例:<br>

 *         Thread    File Name File Size

 *         Thread-1 test_1.mp3 300kb

 *         Thread-2 test_2.mp3 300kb

 *         Thread-3 test_3.mp3 200kb

 *

 *        最终的文件:test.mp3

 */

publicclass ThreadDownloadFile {

      // 要下载的文件的URL

      private StringfileUrl;

      // 要保存的文件名

      private StringsaveName;

      // 要创建的线程数量

      privatestaticfinalintTHREAD_COUNT = 5;

      // 保存线程运行的状态(0标识线程还在运行,1标识结束)

      Map<Integer, Integer> threadStatusMap =new HashMap<Integer, Integer>();

      

      public ThreadDownloadFile(String fileUrl,String saveName) {

             this.fileUrl = fileUrl;

             this.saveName = saveName;

             // 初始化线程运行状态

             for (int i=0;i<THREAD_COUNT;i++) {

                    // key:线程编号,value:线程运行状态

                    threadStatusMap.put(i, 0);

             }

      }

 

      privatevoid download()throws IOException {

             URL url = new URL(this.fileUrl);

             HttpURLConnection conn = (HttpURLConnection) url.openConnection();

             conn.setAllowUserInteraction(true);

             conn.connect();

             // 文件总的长度

             int contentLength = conn.getContentLength();

             // 每个线程应该分配的长度

             int partLength = contentLength /this.THREAD_COUNT + 1;

             

             conn.disconnect();

             

             System.out.println("开始下载文件...");

 

             for (int i = 0; i <this.THREAD_COUNT; i++) {

                    int length = partLength;

                    if (i ==this.THREAD_COUNT - 1) {// 最后一个的长度

                           length = contentLength - partLength * i;

                    }

                    int index1 =saveName.lastIndexOf("/");

                    int index2 =saveName.lastIndexOf(".");

                    String partFileName = saveName.substring(0, index1+1)

                                  + saveName.substring(index1+1, index2) +"_"+(i + 1)

                                  + saveName.substring(index2,saveName.length());

                    DownloadThread dt = new DownloadThread(conn,contentLength,this.THREAD_COUNT,i,length,i*length, url, partFileName,threadStatusMap,this.saveName);

                    dt.start();

             }

      }

 

      /**

       * @param args

       */

      publicstaticvoid main(String[] args) {

//           String saveName = "http://www.a.b/c/d.mp3";

//           int index1 = saveName.lastIndexOf("/");

//           int index2 = saveName.lastIndexOf(".");

//           int i = 0;

//           String partFileName = saveName.substring(0, index1+1)

//                         + saveName.substring(index1+1, index2) +"_"+ (i + 1)

//                         + saveName.substring(index2, saveName.length());

//           System.out.println(partFileName);

             String fileUrl = "http://www.baidu.com";//"http://localhost:8080/filedownload/download1.jsp";

             String saveName = "D:/Users/ex-qincidong001/Desktop/outlook邮件归类.docx";

             ThreadDownloadFile tdf = new ThreadDownloadFile(fileUrl,saveName);

             try {

                    tdf.download();

             } catch (IOException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

             }

      }

 

}

 

class DownloadThreadextends Thread {

      // 总长度

      privateintcontentLength;

      // 线程数

      privateintthreadNum;

      // 当前线程编号

      privateintcurrentThreadNum;

      privateintlength;

      privateintoffset;

      private URLurl;

      private StringpartFileName;

      // 要保存的文件名

      private StringsaveName;

      private Map<Integer,Integer>threadStatusMap;

      HttpURLConnection conn;

 

      DownloadThread(HttpURLConnection conn,int contentLength,int threadNum,int currentThreadNum,int length,int offset, URL url, String partFileName,Map<Integer,Integer> threadStatusMap,String saveName) {

             this.conn = conn;

             this.contentLength = contentLength;

             this.threadNum = threadNum;

             this.currentThreadNum = currentThreadNum;

             this.length = length;

             this.offset = offset;

             this.url = url;

             this.partFileName = partFileName;

             this.threadStatusMap = threadStatusMap;

             this.saveName = saveName;

      }

 

      publicvoid run() {

             System.out.println("线程【" +this.currentThreadNum + "】开启...");

//           HttpURLConnection conn;

             try {

                    conn = (HttpURLConnection)url.openConnection();

                    conn.setAllowUserInteraction(true);

                    conn.setRequestProperty("RANGE","bytes=" + offset );

                    conn.connect();

                    // 设置断点续传的开始位置

                    

                    FileOutputStream fos = new FileOutputStream(partFileName);

                    InputStream is = conn.getInputStream();

                    byte[] data =newbyte[1024];

                    int len = -1;

                    int readSize = 0;

 

                    while ((len = is.read(data)) != -1) {

                           readSize += len;

                           

                           if (readSize >length) { // 只读取length长度

                                  len = len - (readSize - length);

                           }

                           fos.write(data, 0, len);

                           

                           if (readSize >length) {

                                  break;

                           }

                    }

 

                    fos.flush();

                    is.close();

                    // 将线程运行状态改为1(结束)

                    this.threadStatusMap.remove(currentThreadNum);

                    this.threadStatusMap.put(currentThreadNum, 1);

                    

                    // 检查是否全部想线程都运行完毕

                    boolean flag =true;

                    Set<Integer> keys = this.threadStatusMap.keySet();

                    Iterator<Integer> its = keys.iterator();

                    while (its.hasNext()) {

                           Integer key = its.next();

                           Integer value = this.threadStatusMap.get(key);

                           if (value == 0) {

                                  flag = false;

                                  break;

                           }

                    }

                    

                    System.out.println("线程【" +this.currentThreadNum + "】结束...");

                    

                    if (flag) {// 所有的下载线程均结束,可以开始合并文件的线程

                           MergeThread mt = new MergeThread(this.contentLength,this.threadNum,this.saveName);

                           mt.start();

                           System.out.println("文件下载完毕...\n文件保存位置:" +this.saveName);

                    }

             } catch (IOException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

             }

 

      }

}

 

/**

 *合并文件的线程。

 *@author EX-QINCIDONG001

 *

 */

class MergeThreadextends Thread {

      // 文件总长度

      privateintcontentLength;

      // 线程的数量,以便于知道,生成了几个子文件。

      privateintthreadNum;

      // 要保存的最终的文件名

      private StringsaveName;

      

      public MergeThread(int contentLength,int threadNum,String saveName) {

             this.contentLength = contentLength;

             this.threadNum = threadNum;

             this.saveName = saveName;

      }

      publicvoid run() {

             int index1 =saveName.lastIndexOf("/");

             int index2 =saveName.lastIndexOf(".");

             RandomAccessFile accessFile = null;

             try {

                    accessFile = new RandomAccessFile(saveName,"rw");

             } catch (FileNotFoundException e1) {

                    // TODO Auto-generated catch block

                    e1.printStackTrace();

             }

             // 每个线程应该分配的长度

             int partLength =contentLength /this.threadNum + 1;

             for (int i=0;i<threadNum;i++) {

                    // 子文件文件名

                    String partFileName = saveName.substring(0, index1+1)

                    + saveName.substring(index1+1, index2) +"_"+ (i + 1)

                    + saveName.substring(index2,saveName.length());

                    

                    int length = partLength;

                    if (i ==this.threadNum - 1) {// 最后一个的长度

                           length = contentLength - partLength * i;

                    }

                    

                    try {

                           accessFile.seek(i*length);

                           FileInputStream fis = new FileInputStream(partFileName);

                           byte[] data =newbyte[1024];

                           int len = -1;

                           

                           while ((len = fis.read(data)) != -1) {

                                  accessFile.write(data,0,len);

                           }

                           

                           fis.close();

                           accessFile.close();

                    } catch (FileNotFoundException e) {

                           // TODO Auto-generated catch block

                           e.printStackTrace();

                    } catch (IOException e) {

                           // TODO Auto-generated catch block

                           e.printStackTrace();

                    }

             }

      }

}
原创粉丝点击