【0042】Android基础-30-JavaSE多线程下载(重要)

【概述】

  首先使用JavaSE书写一个多线程下载的实例,然后将该实例移植到Android中;

【1】步骤

【2】获取要下载的文件的大小

 

【3】设置随机文件的大小

 在本地磁盘新建一个与要下载的文件大小相同的空文件;

 

【4】要分配每个线程下载文件的开始位置和结束位置

 

【5】开启线程去执行下载

                通过UrlConnection下载部分资源。
                注意:
                 1.需要Range头,key:Range   value:bytes:0-499 
                        urlconnection.setRequestPropety("Range","bytes:0-499")
                 2.需要设置每个线程在本地文件的保存的开始位置
                        RandomAccessFile randomfile =new RandomAccessFile(File file,String mode)
                        randomfile.seek(int startPostion);//本次线程下载保存的开始位置。

 1     public static class DownloadThread  extends Thread{
 2 
 3 
 4         private int threadId;
 5         private int startIndex;
 6         private int endIndex;
 7         private int lastPostion;
 8         public DownloadThread(int threadId,int startIndex,int endIndex){
 9             this.threadId = threadId;
10             this.startIndex = startIndex;
11             this.endIndex = endIndex;
12         }
13 
14         @Override
15         public void run() {
16                 
17             //分段请求网络连接,分段保存文件到本地
18             try{
19                 URL url = new URL(path);
20                 HttpURLConnection openConnection = (HttpURLConnection) url.openConnection();
21                 openConnection.setRequestMethod("GET");
22                 openConnection.setConnectTimeout(10*1000);
23                     
24                 System.out.println("理论上下载:  线程:"+threadId+",开始位置:"+startIndex+";结束位置:"+endIndex);
25                 
26                         
27                     lastPostion = startIndex;
28                     //设置分段下载的头信息。  Range:做分段数据请求用的。
29                     openConnection.setRequestProperty("Range", "bytes:"+lastPostion+"-"+endIndex);//bytes:0-500:请求服务器资源中0-500之间的字节信息  501-1000:
30                     System.out.println("实际下载:  线程:"+threadId+",开始位置:"+lastPostion+";结束位置:"+endIndex);
31 
32                 if(openConnection.getResponseCode() == 206){//200:请求全部资源成功, 206代表部分资源请求成功
33                     InputStream inputStream = openConnection.getInputStream();
34                     //请求成功将流写入本地文件中,已经创建的占位那个文件中
35                     
36                     RandomAccessFile randomAccessFile = new RandomAccessFile(new File(getFileName(path)), "rw");
37                     randomAccessFile.seek(lastPostion);//设置随机文件从哪个位置开始写。
38                     //将流中的数据写入文件
39                     byte[] buffer = new byte[1024];
40                     int length = -1;
41                     int total = 0;//记录本次线程下载的总大小
42                     
43                     while((length= inputStream.read(buffer)) !=-1){
44                         randomAccessFile.write(buffer, 0, length);
45                         //创建随机文件保存当前线程下载的位置
46                         File file = new File(threadId+".txt");
47                         RandomAccessFile accessfile = new RandomAccessFile(file, "rwd");
48                         accessfile.write(String.valueOf(currentThreadPostion).getBytes());
49                         accessfile.close();
50     
51                     }
52                     //关闭相关的流信息
53                     inputStream.close();
54                     randomAccessFile.close();
55                     
56                     System.out.println("线程:"+threadId+",下载完毕");
57                                         
58                     //当所有线程下载结束,删除存放下载位置的文件。
59                     synchronized (DownloadThread.class) {
60                         runningTrheadCount = runningTrheadCount -1;//标志着一个线程下载结束。
61                         if(runningTrheadCount == 0 ){
62                             System.out.println("所有线程下载完成");
63                             for(int i =0 ;i< threadCount;i++){
64                                 File file = new File(i+".txt");
65                                 System.out.println(file.getAbsolutePath());
66                                 file.delete();
67                             }
68                         }            
69                     }
70                             
71                 }    
72 
73             }catch (Exception e) {
74                 e.printStackTrace();
75             }
76             super.run();
77         }
78 
79     }
80 }

【6】断点续传的功能:

【6.1】断点位置的保存

【6.2】断点信息在下次开始下载时读取

【关键】计算好每次开始断点下载的位置与下载开始下载的位置要衔接好;

【7】注意-每次下载完成之后需要删除原来的显示的进度的文件

【8】源码

  1 package com.itheima.muchthreaddown;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.File;
  5 import java.io.FileInputStream;
  6 import java.io.FileOutputStream;
  7 import java.io.InputStream;
  8 import java.io.InputStreamReader;
  9 import java.io.RandomAccessFile;
 10 import java.net.HttpURLConnection;
 11 import java.net.URL;
 12 import java.net.URLConnection;
 13 
 14 public class MuchThreadDown {
 15 
 16     private static int threadCount = 3;// 开启3个线程
 17     private static int blockSize = 0;// 每个线程下载的大小
 18     private static int runningTrheadCount = 0;// 当前运行的线程数
 19     private static String path = "http://192.168.13.83:8080/itheima74/feiq.exe";
 20 
 21     /**
 22      * @param args
 23      */
 24     public static void main(String[] args) {
 25 
 26         try {
 27             // 1.请求url地址获取服务端资源的大小
 28             URL url = new URL(path);
 29             HttpURLConnection openConnection = (HttpURLConnection) url
 30                     .openConnection();
 31             openConnection.setRequestMethod("GET");
 32             openConnection.setConnectTimeout(10 * 1000);
 33 
 34             int code = openConnection.getResponseCode();
 35             if (code == 200) {
 36                 // 获取资源的大小
 37                 int filelength = openConnection.getContentLength();
 38                 // 2.在本地创建一个与服务端资源同样大小的一个文件(占位)
 39                 RandomAccessFile randomAccessFile = new RandomAccessFile(
 40                         new File(getFileName(path)), "rw");
 41                 randomAccessFile.setLength(filelength);// 设置随机访问文件的大小
 42 
 43                 // 3.要分配每个线程下载文件的开始位置和结束位置。
 44                 blockSize = filelength / threadCount;// 计算出每个线程理论下载大小
 45                 for (int threadId = 0; threadId < threadCount; threadId++) {
 46                     int startIndex = threadId * blockSize;// 计算每个线程下载的开始位置
 47                     int endIndex = (threadId + 1) * blockSize - 1;// 计算每个线程下载的结束位置
 48                     // 如果是最后一个线程,结束位置需要单独计算
 49                     if (threadId == threadCount - 1) {
 50                         endIndex = filelength - 1;
 51                     }
 52 
 53                     // 4.开启线程去执行下载
 54                     new DownloadThread(threadId, startIndex, endIndex).start();
 55 
 56                 }
 57 
 58             }
 59 
 60         } catch (Exception e) {
 61             e.printStackTrace();
 62         }
 63 
 64     }
 65 
 66     public static class DownloadThread extends Thread {
 67 
 68         private int threadId;
 69         private int startIndex;
 70         private int endIndex;
 71         private int lastPostion;
 72 
 73         public DownloadThread(int threadId, int startIndex, int endIndex) {
 74             this.threadId = threadId;
 75             this.startIndex = startIndex;
 76             this.endIndex = endIndex;
 77         }
 78 
 79         @Override
 80         public void run() {
 81 
 82             synchronized (DownloadThread.class) {
 83 
 84                 runningTrheadCount = runningTrheadCount + 1;// 开启一线程,线程数加1
 85             }
 86 
 87             // 分段请求网络连接,分段保存文件到本地
 88             try {
 89                 URL url = new URL(path);
 90                 HttpURLConnection openConnection = (HttpURLConnection) url
 91                         .openConnection();
 92                 openConnection.setRequestMethod("GET");
 93                 openConnection.setConnectTimeout(10 * 1000);
 94 
 95                 System.out.println("理论上下载:  线程:" + threadId + ",开始位置:"
 96                         + startIndex + ";结束位置:" + endIndex);
 97 
 98                 // 读取上次下载结束的位置,本次从这个位置开始直接下载。
 99                 File file2 = new File(threadId + ".txt");
100                 if (file2.exists()) {
101                     BufferedReader bufferedReader = new BufferedReader(
102                             new InputStreamReader(new FileInputStream(file2)));
103                     String lastPostion_str = bufferedReader.readLine();
104                     lastPostion = Integer.parseInt(lastPostion_str);// 读取文件获取上次下载的位置
105 
106                     // 设置分段下载的头信息。 Range:做分段数据请求用的。
107                     openConnection.setRequestProperty("Range", "bytes:"
108                             + lastPostion + "-" + endIndex);// bytes:0-500:请求服务器资源中0-500之间的字节信息
109                                                             // 501-1000:
110                     System.out.println("实际下载:  线程:" + threadId + ",开始位置:"
111                             + lastPostion + ";结束位置:" + endIndex);
112                     bufferedReader.close();
113                 } else {
114 
115                     lastPostion = startIndex;
116                     // 设置分段下载的头信息。 Range:做分段数据请求用的。
117                     openConnection.setRequestProperty("Range", "bytes:"
118                             + lastPostion + "-" + endIndex);// bytes:0-500:请求服务器资源中0-500之间的字节信息
119                                                             // 501-1000:
120                     System.out.println("实际下载:  线程:" + threadId + ",开始位置:"
121                             + lastPostion + ";结束位置:" + endIndex);
122                 }
123 
124                 if (openConnection.getResponseCode() == 206) {// 200:请求全部资源成功,
125                                                                 // 206代表部分资源请求成功
126                     InputStream inputStream = openConnection.getInputStream();
127                     // 请求成功将流写入本地文件中,已经创建的占位那个文件中
128 
129                     RandomAccessFile randomAccessFile = new RandomAccessFile(
130                             new File(getFileName(path)), "rw");
131                     randomAccessFile.seek(lastPostion);// 设置随机文件从哪个位置开始写。
132                     // 将流中的数据写入文件
133                     byte[] buffer = new byte[1024];
134                     int length = -1;
135                     int total = 0;// 记录本次线程下载的总大小
136 
137                     while ((length = inputStream.read(buffer)) != -1) {
138                         randomAccessFile.write(buffer, 0, length);
139 
140                         total = total + length;
141                         // 去保存当前线程下载的位置,保存到文件中
142                         int currentThreadPostion = lastPostion + total;// 计算出当前线程本次下载的位置
143                         // 创建随机文件保存当前线程下载的位置
144                         File file = new File(threadId + ".txt");
145                         RandomAccessFile accessfile = new RandomAccessFile(
146                                 file, "rwd");
147                         accessfile.write(String.valueOf(currentThreadPostion)
148                                 .getBytes());
149                         accessfile.close();
150 
151                     }
152                     // 关闭相关的流信息
153                     inputStream.close();
154                     randomAccessFile.close();
155 
156                     System.out.println("线程:" + threadId + ",下载完毕");
157 
158                     // 当所有线程下载结束,删除存放下载位置的文件。
159                     synchronized (DownloadThread.class) {
160                         runningTrheadCount = runningTrheadCount - 1;// 标志着一个线程下载结束。
161                         if (runningTrheadCount == 0) {
162                             System.out.println("所有线程下载完成");
163                             for (int i = 0; i < threadCount; i++) {
164                                 File file = new File(i + ".txt");
165                                 System.out.println(file.getAbsolutePath());
166                                 file.delete();
167                             }
168                         }
169 
170                     }
171 
172                 }
173 
174             } catch (Exception e) {
175                 e.printStackTrace();
176             }
177 
178             super.run();
179         }
180 
181     }
182 
183     public static String getFileName(String url) {//通过找到最后一个斜杠的位置,然后提取出文件的名称;
184 
185         return url.substring(url.lastIndexOf("/"));
186 
187     }
188 
189 }

 【9】注意事项:

   (1)在下次开始下载时候的位置应该从上次记录下载的位置开始下载:应该是lastPostion,不是startPostion;

      randomAccessFile.seek(lastPostion);// 设置随机文件从哪个位置开始写。

   (2)在文件下载结束之后需要将保存位置的文件删除,在删除前应该关闭文件流,否则文件无法删除;

1 bufferedReader.close();

 

   (3) 为了使程序更通用,写了 专门的提取文件名称的方法,从url地址中提取

1  RandomAccessFile randomAccessFile = new RandomAccessFile(new File(getFileName(path)), "rw");
3 ---------------------------------------------------
4 public static String getFileName(String url)//通过找到最后一个斜杠的位置,然后提取出文件的名称;

(4)在线程个数读取的时候需要注意锁,多个线程同时读取数据或者更改数据容易出现数据不准确

 【10】完整源码下载地址:http://pan.baidu.com/s/1jIKho8I

posted @ 2017-10-25 17:01  OzTaking  阅读(211)  评论(0)    收藏  举报