package org.example;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FileDownLoadTest {
// 线程数
private static final int TCOUNT = 10;
// 一种同步辅助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
private CountDownLatch latch = new CountDownLatch(TCOUNT);
// 当前已完成下载长度
private long completeLength = 0;
// 文件总长度
private long fileLength;
public static void main(String[] args) throws Exception {
long begin = System.currentTimeMillis();
new FileDownLoadTest().download("https://down.qq.com/qqweb/PCQQ/PCQQ_EXE/PCQQ2021.exe");
System.out.println("下载时长"+(System.currentTimeMillis() - begin));
}
public void download(String address) throws Exception{
// 拿到线程池
ExecutorService service = Executors.newFixedThreadPool(TCOUNT);
// 定位资源
URL url = new URL(address);
// 获得与资源的链接
URLConnection cn = url.openConnection();
// 设置请求头
cn.setRequestProperty("Referer", address);
// 拿到资源数据总大小
fileLength = cn.getContentLength();
// 每个线程下载数据块大小
long packageLength = fileLength/TCOUNT;
// 用来描述第一个数据包开始结束位置
long leftLength = fileLength%TCOUNT;
// 随机访问文件,可读可写模式
RandomAccessFile file = new RandomAccessFile("test.exe","rw");
// 数据块下载起始指针
long pos = 0;
for(int i=0; i<TCOUNT; i++){
// 数据块下载结束指针
long endPos = pos + packageLength;
while (leftLength >0){
endPos ++;
leftLength--;
}
// 执行10个下载线程
service.execute(new DownLoadThread(url, file, pos, endPos));
// 每个下载线程完成后重置指针位置
pos = endPos;
}
// 等待所有线程完成
latch.await();
}
class DownLoadThread implements Runnable{
private URL url;
private RandomAccessFile file;
private long from;
private long end;
DownLoadThread(URL url, RandomAccessFile file, long from, long end){
this.url = url;
this.file = file;
this.from = from;
this.end = end;
}
public void run() {
// 下载指针开始的位置
long pos = from;
// 缓冲字节组
byte[] buf = new byte[1024*8];
try {
// 打开链接
HttpURLConnection cn = (HttpURLConnection) url.openConnection();
// 设置分批次下载请求头
cn.setRequestProperty("Range", "bytes=" + from + "-" + end);
// !=200 表示支持分片下载,!=206表示没有处理部分get请求
if(cn.getResponseCode() != 200 && cn.getResponseCode()!=206){
run();
return;
}
// 缓冲字节输入流
BufferedInputStream bis = new BufferedInputStream(cn.getInputStream());
// 要写入的字节长度
int len ;
while((len = bis.read(buf)) != -1){
synchronized(file){
// 设置游标位置
file.seek(pos);
// 设置写入字节长度
file.write(buf, 0, len);
}
// 游标位置右移len个长度
pos += len;
// 已下载长度
completeLength +=len;
System.out.println(completeLength * 100 /fileLength + "%");
}
// 关闭连接
cn.disconnect();
// 线程未完成数-1
latch.countDown();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}