zoukankan      html  css  js  c++  java
  • 多线程文件下载

    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();
                }
            }
        }
    }
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利
  • 相关阅读:
    crt中文乱码
    idea使用git版本控制忽略部分文件
    Springboot配置redis连接池参数
    redis通过lua脚本实现分布式锁
    jodaTime的使用
    jmap的使用
    代理服务器没有响应解决方案
    C/C/C++中暂停调试的三种方法C++中暂停调试的三种方法
    C/C/C++中暂停调试的三种方法C++中暂停调试的三种方法
    四种常见的post请求中的参数形式
  • 原文地址:https://www.cnblogs.com/hhddd-1024/p/14608959.html
Copyright © 2011-2022 走看看