多线程下载

2019-08-09 00:00:00 多线程 下载

废话不多说,直接上代码

  1 import java.io.File;
  2 import java.io.InputStream;
  3 import java.io.RandomAccessFile;
  4 import java.net.HttpURLConnection;
  5 import java.net.URL;
  6 
  7 public class DownUtil {
  8     //定义下载资源的路径
  9     private String path;
 10     //指定所下载的文件的保存位置
 11     private String targetFile;
 12     //定义需要使用多少个线程下载资源
 13     private int threadNum;
 14     //定义下载的线程对象
 15     private DownThread[] threads;
 16     //定义下载的文件的总大小
 17     private int fileSize;
 18 
 19     //构造器
 20     public DownUtil(String path, String targetFile, int threadNum) {
 21         this.path = path;
 22         this.threadNum = threadNum;
 23         //初始化threads数组
 24         threads = new DownThread[threadNum];
 25         this.targetFile = targetFile;
 26     }
 27 
 28     public void download() throws Exception {
 29         URL url = new URL(path);
 30         //1.通过调用URL对象的openConnection()方法来创建URLConnection对象
 31         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 32         //2.设置URLConnection的参数和普通请求属性
 33         conn.setConnectTimeout(5 * 1000);
 34         conn.setRequestMethod("GET");
 35 
 36         //得到文件大小
 37         fileSize = conn.getContentLength();
 38         conn.disconnect();
 39         int currentPartSize = fileSize / threadNum + 1;//每个线程所要处理任务大小
 40         File tarFile = new File(targetFile.substring(0, targetFile.lastIndexOf("/")));
 41         if (!tarFile.exists()) {
 42             tarFile.mkdirs();
 43         }
 44         RandomAccessFile file = new RandomAccessFile(targetFile, "rw");
 45         //设置本地文件的大小
 46         file.setLength(fileSize);
 47         file.close();
 48         for (int i = 0; i < threadNum; i++) {
 49             //计算每个线程下载开始的位置
 50             int startPos = i * currentPartSize;
 51             //每个线程使用一个RandomAccessFile进行下载
 52             RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
 53             //定位该线程的下载位置
 54             currentPart.seek(startPos);
 55             //创建下载线程
 56             threads[i] = new DownThread(startPos, currentPartSize, currentPart);
 57             //启动下载线程
 58             threads[i].start();
 59         }
 60     }
 61 
 62     //获取下载的完成百分比
 63     public double getCompleteRate() {
 64         //统计多个线程已经下载的总大小
 65         int sumSize = 0;
 66         for (int i = 0; i < threadNum; i++) {
 67             sumSize += threads[i].length;
 68         }
 69         //返回已经完成的百分比
 70         return sumSize * 1.0 / fileSize;
 71     }
 72 
 73     public class DownThread extends Thread {
 74         //当前线程的下载位置
 75         private int startPos;
 76         //定义当前线程负责下载的文件大小
 77         private int currentPartSize;
 78         //当前线程需要下载的文件块
 79         private RandomAccessFile currentPart;
 80         //定义该线程已下载的字节数
 81         public int length;
 82 
 83         //构造器
 84         public DownThread(int startPos, int currentPartSize, RandomAccessFile currentPart) {
 85             this.startPos = startPos;
 86             this.currentPartSize = currentPartSize;
 87             this.currentPart = currentPart;
 88         }
 89 
 90         //下载线程的主函数体
 91         public void run() {
 92             try {
 93                 URL url = new URL(path);
 94                 //1.通过调用URL对象的openConnection()方法来创建URLConnection对象
 95                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 96                 //2.设置URLConnection的参数和普通请求属性
 97                 conn.setConnectTimeout(5 * 1000);
 98                 conn.setRequestMethod("GET");
 99                 //4.远程资源变为可用,程序可以通过输入流读取远程资源的数据
100                 InputStream inStream = conn.getInputStream();
101 
102                 //跳过stratPos个字节,表明该线程只下载自己负责的那部分文件
103                 //同时每个线程都会在指定的文件区域写入,所以不会因为多线程而出
104                 //现资源组合的错乱,从指定位置读取资源,写入到指定位置
105                 inStream.skip(this.startPos);
106                 byte[] buffer = new byte[1024];//自己设置一个缓冲区
107                 int hasread;
108 
109                 //-----------------读取网路数据,并写入本地文件-------------------
110                 //inStream.read(buffer))==-1 表示读取到文件末尾
111                 while (length < currentPartSize && (hasread = inStream.read(buffer)) != -1) {
112                     currentPart.write(buffer, 0, hasread);
113                     //累计该线程下载的总大小
114                     length += hasread;
115                     //System.out.println(getName()+" "+hasread);
116                 }
117                 //System.out.println(getName()+" length "+length+" currentPartSize "+currentPartSize);
118                 //即使length>currentPartSize会是的该线程多写入几个字节,
119                 //但是下一个线程会从文件的指定位置写入,就会覆盖掉之前线程多写的一部分内容
120                 currentPart.close();
121                 inStream.close();
122             } catch (Exception e) {
123                 e.printStackTrace();
124             }
125         }
126 
127     }
128 }

调用上述下载工具类

 1 public static String jarurl = "http://softdown1.hao123.com/hao123-soft-online-bcs/soft/2017_09_29_jpwb2017qj.exe"; // 下载地址
 2  private Thread processThread = new Thread();
 3  public static boolean downloaded = false;//下载完成与否
 4  public static boolean errored = false;//下载出错与否
 5  public void dowload() {
 6         try {
 7             if (HttpUtil.isConnect(jarurl)) {
 8                 //初始化DownUtil对象
 9                 //配置4个线程去下载数据
10                 //第二个参数文件名可随意更改,下载到本地项目中
11                 final DownUtil downUtil = new DownUtil(jarurl,"rct-demo.exe", 4);
12                 //开始下载
13                 downUtil.download();
14                 //启动线程获取下载进度
15                 processThread = new Thread(() ->
16                 {
17                     while (downUtil.getCompleteRate() < 1) {
18                         //每隔0.1秒查询一次任务的完成进度
19                         log.info("已完成:{}", downUtil.getCompleteRate());
20                         try {
21                             Thread.sleep(100);
22                         } catch (Exception ex) {
23                         }
24                     }
25                     log.info("已完成:{}", downUtil.getCompleteRate() >= 1 ? "1.0" : downUtil.getCompleteRate());
26                     if (downUtil.getCompleteRate() >= 1) {
27                        //下载完成的一些操作
28                     }
29                 });
30                 processThread.start();
31             }
32         } catch (Exception e) {
33             downloaded = false;
34             errored = true;
35             e.printStackTrace();
36         }
37     }

判定连接是否可用

 1  private static URL url;
 2  private static HttpURLConnection con;
 3  private static int state = -1;
 4  /**
 5      * 检测当前URL是否可连接或是否有效,
 6      *
 7      * @param urlStr 指定URL网络地址
 8      * @return URL
 9      */
10     public synchronized static boolean isConnect(String urlStr) {
11         if (urlStr == null || urlStr.length() <= 0) {
12             return false;
13         }
14         try {
15             url = new URL(urlStr);
16             con = (HttpURLConnection) url.openConnection();
17             state = con.getResponseCode();
18             if (state == 200) {
19                 log.info("{}地址可用!", urlStr);
20                 return true;
21             }
22         } catch (Exception ex) {
23             log.info("{}地址不可用", urlStr);
24         }
25         return false;
26     }

 

相关文章