一、多线程加速下载下载
1.不是说线程开的越多下载就越快 例:手机迅雷(建议3-4个线程)
2.还受服务器带宽的影响
3.相当于更多的cpu资源给了你
二、多线程下载步骤分析
1.获取文件大小
2.在客户端创建一个大小和服务器一模一样的文件,提前申请好空间
3.每个线程下载的开始位置和结束位置
4.开多个线程去下载文件
4.知道每个线程什么时候下载完毕了
1.获取文件的大小
public static void main(String[] args) {
//【一★★★★★★★】获取服务器文件大小
new Thread() {
@Override
public void run() {
try {
//【2】定义下载路径
String path = "http://192.168.3.138:8080/ttt.exe";
//【2.2】创建URL对象指定我们要访问的网址(路径)
URL url = new URL(path);
//【2.3】拿到HttpURLConnection对象 用于发送或接受数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//【2.4】设置发送get请求
conn.setRequestMethod("GET");
//【2.5】设置请求时间
conn.setConnectTimeout(5000);
//【2.6】获取服务器返回的状态码
int code = conn.getResponseCode();
//【2.7】如果code == 200说明请求成功
if (code == 200) {
//★★★★★获取服务器文件大小
int length = conn.getContentLength();
System.out.println("length:"+length);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
2.每个线程下载的计算公式
3.实现代码
package down;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class MutioDown {
// 【1】定义下载路径
static String path = "http://192.168.3.138:8080/ttt.exe";
private static final int threadCount = 3;// 假设开三个线程
private static int runningThread;
public static void main(String[] args) {
try {
// 【2】创建URL对象指定我们要访问的网址(路径)
URL url = new URL(path);
// 【3】拿到HttpURLConnection对象 用于发送或接受数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 【4】设置发送get请求
conn.setRequestMethod("GET");
// 【5】设置请求时间
conn.setConnectTimeout(5000);
// 【6】获取服务器返回的状态码
int code = conn.getResponseCode();
// 【6.1】如果code == 200说明请求成功
if (code == 200) {
// 【7】获取服务器文件大小
int length = conn.getContentLength();
runningThread = threadCount;
System.out.println("length:" + length);
// 【二★★★★★★★】创建一个大小和服务器一模一样的文件,目的提前把空间申请出来
RandomAccessFile rafAccessFile = new RandomAccessFile(
getFilename(path), "rw");
rafAccessFile.setLength(length);
// 【7】计算出每个线程下载的大小
int blockSize = length / threadCount;
// 【三★★★★★★★】计算每个线程下载的开始位置和结束位置
for (int i = 0; i < threadCount; i++) {
int starIndex = i * blockSize; // 每个线程下载的开始位置
int endIndex = (i + 1) * blockSize - 1;// 每个线程下载的结束位置
// 特殊情况 就是最后一个线程
if (i == threadCount - 1) {
endIndex = length - 1;
}
// 【四★★★★★★★】开启线程去服务器下载文件
new DownLoadThread(starIndex, endIndex, i).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static class DownLoadThread extends Thread {
// 通过构造方法把每个线程下载的开始位置和结束位置传递进来
private int starIndex;
private int endIndex;
private int threadId;
public DownLoadThread(int starIndex, int endIndex, int threadId) {
this.starIndex = starIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
// 【四★★★★★★】实现去服务器下载的逻辑
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
// 如果中间断过,继续在上次的位置继续下载,从文件中读取上次下载的位置
File file = new File(getFilename(path) + threadId + ".txt");
if (file.exists() && file.length() > 0) {
FileInputStream fis = new FileInputStream(file);
BufferedReader bufr = new BufferedReader(
new InputStreamReader(fis));
String lastPositionn = bufr.readLine(); // 读取出来的内容就是上次下载的位置
int lastPosition = Integer.parseInt(lastPositionn);
// 要改变一下startIndex的位置
starIndex = lastPosition;
fis.close();
}
// ★★★★★设置一个请求头Range(作用告诉服务器每个线程下载的开始位置和结束位置)
conn.setRequestProperty("Range", "bytes=" + starIndex + "-"
+ endIndex);
int code = conn.getResponseCode();
if (code == 206) { // 200表示获取服务器资源全部成功,206表示请求部分资源成功
// 创建随机读写文件对象
RandomAccessFile raf = new RandomAccessFile(
getFilename(path), "rw");
// 每个线程要从自己的位置开始写
raf.seek(starIndex);
InputStream in = conn.getInputStream(); // 存的是ttt.exe
// 把数据写到文件中
int len = -1;
byte[] buffer = new byte[1024 * 1024];
int total = 0; // 代表当前线程下载的大小
while ((len = in.read(buffer)) != -1) {
raf.write(buffer, 0, len);
// 实现断点续传 就是把当前线程下载的位置存起来 下次再下载的时候按上次下载的位置继续下载就可以了
total += len;
int currentThreadPosition = starIndex = total;
RandomAccessFile raff = new RandomAccessFile(
getFilename(path) + threadId + ".txt", "rwd");
raff.write(String.valueOf(currentThreadPosition)
.getBytes());
raff.close();
}
raf.close();// 释放资源
System.out.println("线程:" + threadId + "--下载完了");
synchronized (DownLoadThread.class) {
runningThread--;
if (runningThread == 0) { // 说明所有线程都执行完毕了
for (int i = 0; i < threadCount; i++) {
File deleFile = new File(getFilename(path) + i
+ ".txt");
deleFile.delete();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static String getFilename(String path) { // 获取文件的名字
int start = path.lastIndexOf("/") + 1;
return path.substring(start);
}
}