• 多线程实现多线程下载文件


    下载文件的时候,一个大文件切成很多片,用多线程下载,速度会快很多

    阅读代码的时候注意查看代码里面的注释
    想用多线程下载文件,则,

    第一:得了解  RandomAccessFile  类,这是个随机访问文件类,里面可以设置 访问的 开始地址和结束地址,且该类可读可写。

    RandomAccessFile out = new RandomAccessFile(file, "rw"); 则表示,该类可读可写。通过 out.seek(start)  可以定位开始读取的位置。

    第二:既然是网络文件下载,那就必须得了解 URL 类,该类是 java.net 包提供的一个 可以用来网络连接的类。

    URL url = new URL(urlLocation); 可以这样实例化该类,然后打开连接,HttpURLConnection conn = (HttpURLConnection) url.openConnection();,还可以设置些别的参数,比如说设置超时,设置访问方法,设置 访问 起始点之类的。

            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");

            conn.setRequestProperty("Range", "bytes=" + start +"-"+end );

    第三:了解线程,这里我们使用 java 1.5之后引入的 concurrent 包里面的  Executors.newCachedThreadPool() 线程池

    第四:最基本的 io读写得知道

    看看代码吧。

    1.为了方便,我写了个工具类,用于提供 Util类用来提供获取 HttpURLConnection 连接

    public class Util {

    // 记录读取了多少,一共读取了多少
    public static long start;
    // 记录文件总大小
    public static long sum;

    /**
    *
    * @Title: getHttpConnection
    * @Description: 获取 url 连接
    * @param: @param urlLocation
    * @param: @return HttpURLConnection实例化对象
    * @param: @throws IOException
    * @return: HttpURLConnection
    * @throws
    */
    public static HttpURLConnection getHttpConnection(String urlLocation) throws IOException {
    URL url = new URL(urlLocation);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setConnectTimeout(5000);
    conn.setRequestMethod("GET");

    return conn;
    }
    }
    2.文件切片,分别指定 起始点,注意这里的起始点是包头也包尾的,0-10/11-20/21-30  这种

    public class DownloadFilePool {
    // 网络资源路径
    private String urlLocation;
    // 存储路径
    private String filePath;
    // 多少个线程
    private int poolLength;

    public DownloadFilePool(String urlLocation, String filePath, int poolLength) {
    super();
    // 如果 保存路径为空则默认存在 D盘,文件名跟下载名相同
    if( filePath==null ) {
    String fileName = urlLocation.substring( urlLocation.lastIndexOf("/") +1);
    filePath = "D:/" + fileName;
    }

    this.urlLocation = urlLocation;
    this.filePath = filePath;
    this.poolLength = poolLength;
    }

    public void getFile() {
    try {
    // 获取文件长度
    long fileLength = Util.getHttpConnection(urlLocation).getContentLengthLong();
    Util.sum = fileLength;

    ExecutorService pool = Executors.newCachedThreadPool();

    // 获取每片大小
    long slice = fileLength/poolLength;
    for(int i = 0 ;i < poolLength; i++) {
    long start = i*slice;
    long end = (i+1)*slice -1;

    if(i==poolLength-1) {
    start = i*slice;
    end = fileLength ;
    }
    System.out.println( start + "---" + end );
    // 创建下载类
    DownloadFileRang downloadFileRang = new DownloadFileRang(start, end, urlLocation, filePath);
    // 执行线程
    pool.execute(downloadFileRang);
    }
    // 关闭线程池
    pool.shutdown();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    3.文件下载类,此处使用 继承 Runnable 实现多线程

    public class DownloadFileRang implements Runnable {
    // 文件开始位置
    private long start ;
    // 文件结束位置
    private long end;
    // url地址
    private String urlLocation;
    // 文件存储位置
    private String filePath;

    public DownloadFileRang(long start, long end, String urlLocation, String filePath) {
    super();
    this.start = start;
    this.end = end;
    this.urlLocation = urlLocation;
    this.filePath = filePath;
    }

    @Override
    public void run() {
    try {
    // 获取连接
    HttpURLConnection conn = Util.getHttpConnection(urlLocation);
    // 设置获取资源范围
    conn.setRequestProperty("Range", "bytes=" + start +"-"+end );

    File file = new File(filePath);
    RandomAccessFile out = null;
    if(file!=null) {
    out = new RandomAccessFile(file, "rw");
    }
    out.seek(start);

    // 获取网络连接的 输入流
    InputStream is = conn.getInputStream();

    byte [] data = new byte[1024];
    int len = 0;
    while( (len = is.read(data))!=-1 ) {
    out.write(data, 0, len);
    synchronized (Util.class) {
    Util.start += len;
    }
    }

    // 关闭连接
    out.close();
    is.close();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    4.实现显示实时网速

    实时网速,其实就是单位时间内,所读取到的 资源,我们这里就是读取到的 len,观察我的代码,可以发现,我在 Util类里面写了两个静态属性,分别是 start 用来记录一共读了多少,一个是 sum,记录总文件大小。

    线程类里面的 out.write 方法里面我 用 start 进行累加 len,这就是记录一共读取了多少资源,因为 多线程不能保证数据的原子性,所以我这里累加的时候,为避免因为线程原因出现数据错误,则进行加锁,加上  Util 对象的锁,告诉别的线程,这个 strat 同意时刻内只能一个线程来进行 操作。然后  用 每个时间段,比如说我这里是 500ms ,内读取的资源 / 500ms 就是网速了。

    5.调用实例

    public static void main(String[] args) {
    Date startDate = new Date();

    DownloadFilePool pool = new DownloadFilePool("http://fs.w.kugou.com/201809152325/5cbbb70b45431a17cad6ddd6d5342ef5/G108/M03/0C/01/rA0DAFk_VuiALN9DADmXB0zHYTA058.mp3", null, 100);
    pool.getFile();

    long old = 0;
    long now = 0;
    while( Util.sum >= Util.start ) {
    now = Util.start - old;
    old = Util.start;

    if(Util.sum == Util.start) {
    long t = new Date().getTime() - startDate.getTime();
    double speed = ((double)Util.sum / (t/1000.0))/1024.0/1024.0;

    System.out.println( "下载完成,用时:" + t/1000.0 +" s 平均网速:" + speed +" M/s" );
    break;
    }

    System.out.println( "网速:" + ((double)(now/0.5))/1024.0/1024.0 +" M/s,已完成:" + (Util.start / (double)Util.sum)*100 +"%" );

    try {
    Thread.sleep(500);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
     

    运行效果:
    ---------------------
    作者:临窗,听雨声
    来源:CSDN
    原文:https://blog.csdn.net/yali_aini/article/details/82713368
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    Linux下如何查看版本信息
    SUSE12Sp3安装配置.net core 生产环境(2)-安装.NET Core 2.2.1 runtime 并运行.NET代码
    NET CORE Learning
    JWT(JSON Web Token)原理简介
    定性分析与定量分析的异同及优缺点
    《React16免费基础视频教程》【2】
    《React16免费基础视频教程》【1】
    《狂神说-JUC》【3】
    《狂神说-JUC》【2】
    《狂神说-JUC》【1】
  • 原文地址:https://www.cnblogs.com/sidesky/p/10489854.html
Copyright © 2020-2023  润新知