• 多线程下载

原理:服务器CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源,所以使用多线程下载的话,速度会更快

JavaSE实现带断点续传的多线程下载步骤:

  1、发送http请求至下载地址,获取要下载的资源文件的大小

  2、根据资源文件的大小,创建一个长度一样的临时文件,用来抢占磁盘空间

  3、计算每个线程要下载的数据大小和开始位置、结束位置,余数都由最后一个线程完成下载,所以最后一个线程的结束位置要写死

  4、再次发送请求,请求要下载的数据区间的数据(判断是否有记录进度的临时文件,有的话就继续上次位置接着下载,没有就从原本开始位置下载)

  5、将下载请求到的数据,存储到临时文件中(新建一个记录下载进度的临时文件)

  6、等所有线程都下载完毕了,就要将之前的记录进度的临时文件删除掉

  1 package com.ahu.multithreaddownload;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.File;
  5 import java.io.FileInputStream;
  6 import java.io.InputStream;
  7 import java.io.InputStreamReader;
  8 import java.io.RandomAccessFile;
  9 import java.net.HttpURLConnection;
 10 import java.net.URL;
 11 
 12 /**
 13  * 带断点续传的多线程下载
 14  * 
 15  * @author ahu_lichang
 16  * 
 17  */
 18 public class MultiThreadDownload {
 19     // 线程数
 20     static int ThreadCount = 4;
 21     static int finishedThread = 0;
 22 
 23     static String path = "http://172.23.13.179:8080/report.ppt";
 24 
 25     public static void main(String[] args) {
 26         try {
 27             // 第一次请求服务器,是为了获取资源文件的大小,从而创建一个相同大小的临时文件,而不是下载资源!
 28             URL url = new URL(path);
 29             HttpURLConnection connection = (HttpURLConnection) url
 30                     .openConnection();
 31             connection.setConnectTimeout(5000);
 32             connection.setReadTimeout(5000);
 33             connection.setRequestMethod("GET");
 34             //connection.connect();
 35             if (connection.getResponseCode() == 200) {
 36                 // 获取要下载文件的大小
 37                 int length = connection.getContentLength();
 38                 // 生成临时文件
 39                 RandomAccessFile raf = new RandomAccessFile(getFileName(path),
 40                         "rwd");
 41                 raf.setLength(length);
 42                 raf.close();
 43                 // 计算每个线程下载的资源大小(有可能存在余数,余数都放在最后一个线程里)
 44                 int size = length / ThreadCount;
 45                 // 计算每个线程下载的开始位置和结束位置
 46                 for (int i = 0; i < ThreadCount; i++) {
 47                     int startIndex = i * size;
 48                     int endIndex = (i + 1) * size - 1;
 49                     // 如果是最后一个线程,必须将结束位置写死
 50                     if (i == ThreadCount - 1) {
 51                         endIndex = length - 1;
 52                     }
 53                     // 开启线程下载资源
 54                     new DownloadThread(startIndex, endIndex, i).start();
 55                 }
 56             }
 57         } catch (Exception e) {
 58             e.printStackTrace();
 59         }
 60 
 61     }
 62 
 63     /**
 64      * 获取服务器中资源的名称
 65      * 
 66      * @param path
 67      * @return
 68      */
 69     public static String getFileName(String path) {
 70         int index = path.lastIndexOf("/");
 71         return path.substring(index + 1);
 72     }
 73 
 74 }
 75 /**
 76  * 下载线程
 77  * @author ahu_lichang
 78  *
 79  */
 80 class DownloadThread extends Thread {
 81     int startIndex;
 82     int endIndex;
 83     int threadId;
 84 
 85     public DownloadThread(int startIndex, int endIndex, int threadId) {
 86         super();
 87         this.startIndex = startIndex;
 88         this.endIndex = endIndex;
 89         this.threadId = threadId;
 90     }
 91 
 92     public void run() {
 93         try {
 94             File progressFile = new File(threadId + ".txt");
 95             //如果存在进度临时文件,就接着上次的后面进行下载
 96             if (progressFile.exists()) {
 97                 FileInputStream fis = new FileInputStream(progressFile);
 98                 BufferedReader br = new BufferedReader(new InputStreamReader(
 99                         fis));
100                 // 得到上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置
101                 startIndex += Integer.parseInt(br.readLine());
102                 fis.close();
103             }
104             System.out.println("线程" + threadId + "下载区间" + startIndex + "---"
105                     + endIndex);
106             // 再次请求服务器,下载资源文件
107             URL url = new URL(MultiThreadDownload.path);
108             HttpURLConnection connection = (HttpURLConnection) url
109                     .openConnection();
110             connection.setConnectTimeout(5000);
111             connection.setReadTimeout(5000);
112             connection.setRequestMethod("GET");
113             // 设置本次所请求的数据区间
114             connection.setRequestProperty("Range", "bytes=" + startIndex + "-"
115                     + endIndex);
116             //connection.connect();
117             // 请求部分数据的响应码是206
118             if (connection.getResponseCode() == 206) {
119                 // 拿到1/3源文件的数据
120                 InputStream is = connection.getInputStream();
121                 byte[] b = new byte[1024];
122                 int len = 0;
123                 int total = 0;// 记录下载到临时文件中数据的大小
124                 // 把拿到的数据写入到临时文件中
125                 RandomAccessFile raf = new RandomAccessFile(
126                         MultiThreadDownload
127                                 .getFileName(MultiThreadDownload.path),
128                         "rwd");
129                 // 把文件的写入位置移动至startIndex。这样才不会覆盖写入
130                 raf.seek(startIndex);
131                 while ((len = is.read(b)) != -1) {
132                     raf.write(b, 0, len);
133                     total += len;
134                     // 生成用来记录下载进度的临时文件
135                     RandomAccessFile progressRaf = new RandomAccessFile(
136                             progressFile, "rwd");
137                     progressRaf.write((total + "").getBytes());
138                     progressRaf.close();
139                 }
140                 System.out.println("线程" + threadId + "下载完毕!!!");
141                 raf.close();
142 
143                 // 全部下载完成后,将临时存放进度的文件删除
144                 MultiThreadDownload.finishedThread++;
145                 // 注意线程安全问题
146                 synchronized (MultiThreadDownload.path) {
147                     if (MultiThreadDownload.finishedThread == MultiThreadDownload.ThreadCount) {
148                         for (int i = 0; i < MultiThreadDownload.ThreadCount; i++) {
149                             File f = new File(i + ".txt");
150                             f.delete();
151                         }
152                         MultiThreadDownload.finishedThread = 0;
153                     }
154                 }
155             }
156         } catch (Exception e) {
157             e.printStackTrace();
158         }
159     }
160 }
View Code

相关文章: