多线程文件下载

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

 

posted @ 2021-04-02 01:30  黄河大道东  阅读(254)  评论(0编辑  收藏  举报