多线程下载已经提高了下载的效率,但是当一些特殊情况发生的时候,我们需要对程序进行处理,这样效率会更高。比如,断电断网等造成下载中断,那么我们下一次又要重新开始下载,这样效率底下,所以我们可以考虑使用断点下载。其原理主要是把每次每个线程的下载状况(已经下载的位置)保存到文件,下次读取出来,从上一次下载的位置继续下载,这样就大大提高了下载的效率。
效果:
开始下载:
下载过程中:
下载过程中,系统临时文件保存已下载的位置:
下载完毕,系统清楚记录下载位置的临时文件:
附代码如下:
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 }