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(); } } } }