Java多线程断点下载

多线程下载已经提高了下载的效率,但是当一些特殊情况发生的时候,我们需要对程序进行处理,这样效率会更高。比如,断电断网等造成下载中断,那么我们下一次又要重新开始下载,这样效率底下,所以我们可以考虑使用断点下载。其原理主要是把每次每个线程的下载状况(已经下载的位置)保存到文件,下次读取出来,从上一次下载的位置继续下载,这样就大大提高了下载的效率。

效果:

开始下载:

下载过程中:

下载过程中,系统临时文件保存已下载的位置:

下载完毕,系统清楚记录下载位置的临时文件:

附代码如下:

  1 import java.io.File;
  2 import java.io.FileInputStream;
  3 import java.io.InputStream;
  4 import java.io.RandomAccessFile;
  5 import java.net.HttpURLConnection;
  6 import java.net.URL;
  7 
  8 /**
  9  * 多线程断点下载示例
 10  * @author YUANYUAN
 11  *
 12  */
 13 public class Demo {
 14     //下载所使用的线程数
 15     private static int threadCount=3;
 16     //当前活动的线程数
 17     private static int activeThread;
 18 
 19     public static void main(String[] args) throws Exception{
 20         //请求服务器的路径
 21         String path="http://192.168.2.114:8080/sqlite.exe";
 22         //构造URL地址
 23         URL url=new URL(path);
 24         //打开连接
 25         HttpURLConnection conn=(HttpURLConnection) url.openConnection();
 26         //设置请求超时的时间
 27         conn.setConnectTimeout(5000);
 28         //设置请求方式
 29         conn.setRequestMethod("GET");
 30         //获取相应码
 31         int code=conn.getResponseCode();
 32         if (code==200) {//请求成功
 33             //获取请求数据的长度
 34             int length=conn.getContentLength();
 35             //在客户端创建一个跟服务器文件大小相同的临时文件
 36             RandomAccessFile raf=new RandomAccessFile("setup.exe", "rwd");
 37             //指定临时文件的长度
 38             raf.setLength(length);
 39             raf.close();
 40             //假设3个线程去下载资源
 41             //平均每一个线程要下载的文件的大小
 42             int blockSize=length/threadCount;
 43             for (int threadId = 1; threadId <= threadCount; threadId++) {
 44                 //当前线程下载数据的开始位置
 45                 int startIndex=blockSize*(threadId-1);
 46                 //当前线程下载数据的结束位置
 47                 int endIndex=blockSize*threadId-1;
 48                 //确定最后一个线程要下载数据的最大位置
 49                 if (threadId==threadCount) {
 50                     endIndex=length;
 51                 }
 52                 //显示下载数据的区间
 53                 System.out.println("线程【"+threadId+"】开始下载:"+startIndex+"---->"+endIndex);
 54                 //开启下载的子线程
 55                 new DownloadThread(path, threadId, startIndex, endIndex).start();
 56                 activeThread++;
 57                 System.out.println("当前活动的线程数:"+activeThread);
 58             }
 59             
 60         }else{//请求失败
 61             System.out.println("服务器异常,下载失败!");
 62         }
 63     }
 64     
 65     /**
 66      * 下载文件的子线程 每一个文件都下载对应的数据
 67      * @author YUANYUAN
 68      *
 69      */
 70     public static class DownloadThread extends Thread{
 71         private String path;
 72         private int threadId;
 73         private int startIndex;
 74         private int endIndex;
 75         
 76         /**
 77          * 构造方法
 78          * @param path 下载文件的路径
 79          * @param threadId 下载文件的线程
 80          * @param startIndex 下载文件开始的位置
 81          * @param endIndex 下载文件结束的位置
 82          */
 83         public DownloadThread(String path, int threadId, int startIndex,
 84                 int endIndex) {
 85             this.path = path;
 86             this.threadId = threadId;
 87             this.startIndex = startIndex;
 88             this.endIndex = endIndex;
 89         }
 90 
 91 
 92 
 93         @Override
 94         public void run() {
 95             //构造URL地址
 96             try {
 97                 
 98                 File tempFile=new File(threadId+".txt");
 99                 //检查记录是否存在,如果存在读取数据
100                 if (tempFile.exists()) {
101                     FileInputStream fis=new FileInputStream(tempFile);
102                     byte[] temp=new byte[1024];
103                     int length=fis.read(temp);
104                     //读取到已经下载的位置
105                     int downloadNewIndex=Integer.parseInt(new String(temp, 0, length));
106                     //设置重新开始下载的开始位置
107                     startIndex=downloadNewIndex;
108                     fis.close();
109                     //显示真实下载数据的区间
110                     System.out.println("线程【"+threadId+"】真实开始下载数据区间:"+startIndex+"---->"+endIndex);
111                 }
112                 
113                 URL url = new URL(path);
114                 HttpURLConnection conn=(HttpURLConnection) url.openConnection();
115                 conn.setConnectTimeout(5000);
116                 conn.setRequestMethod("GET");
117                 //设置请求属性,请求部分资源
118                 conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
119                 int code=conn.getResponseCode();
120                 if (code==206) {//下载部分资源,正常返回的状态码为206
121                     InputStream is=conn.getInputStream();//已经设置了请求的位置,所以返回的是对应的部分资源
122                     //构建随机访问文件
123                     RandomAccessFile raf=new RandomAccessFile("setup.exe", "rwd");
124                     //设置 每一个线程随机写文件开始的位置
125                     raf.seek(startIndex);
126                     //开始写文件
127                     int len=0;
128                     byte[] buffer=new byte[1024];
129                     //该线程已经下载数据的长度
130                     int total=0;
131                     
132                     while((len=is.read(buffer))!=-1){//读取输入流
133                         //记录当前线程已下载数据的长度
134                         RandomAccessFile file=new RandomAccessFile(threadId+".txt","rwd");
135                         raf.write(buffer,0,len);//写文件
136                         total+=len;//更新该线程已下载数据的总长度
137                         System.out.println("线程【"+threadId+"】已下载数据:"+(total+startIndex));
138                         //将已下载数据的位置记录写入到文件
139                         file.write((startIndex+total+"").getBytes());
140                         file.close();
141                     }
142                     is.close();
143                     raf.close();
144                     //提示下载完毕
145                     System.out.println("线程【"+threadId+"】下载完毕");
146                 }
147             } catch (Exception e) {
148                 e.printStackTrace();
149                 System.out.println("线程【"+threadId+"】下载出现异常!!");
150             }finally{
151                 //活动的线程数减少
152                 activeThread--;
153                 if (activeThread==0) {
154                     for (int i = 1; i <= threadCount; i++) {
155                         File tempFile=new File(i+".txt");
156                         tempFile.delete();
157                     }
158                     System.out.println("下载完毕,已清除全部临时文件");
159                 }
160             }
161             
162         }
163     }
164 }

 

posted @ 2015-11-08 00:57  我爱Java  阅读(2347)  评论(0编辑  收藏  举报