第14~16多线程下载器项目

多线程下载器

1 获取文件下载地址

package com.xie;
import java.util.Scanner;
//主类
public class Main {
    public static void main(String[] args) {
        //下载地址
        String url=null;
        if(args ==null || args.length==0){
            for (; ;){
                System.out.println("请输入下载地址");
                Scanner scanner = new Scanner(System.in);
                url= scanner.next();
                if (url!=null ){
                    break;
                }
            }
        }else {
            url = args[0];
        }
        System.out.println("下载地址为:"+url);
    }
}

2 文件下载器的基本代码

package com.xie.util;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
 * 获取HttpURLConnection链接对象
 * url文件地址
 * */
public class HttpUtils {
    public static HttpURLConnection getHttpConnection(String url) throws IOException {
        URL httpUrl = new URL(url);
        // 打开URL连接并强制转换为HttpURLConnection类型
        HttpURLConnection httpUrlConnection =(HttpURLConnection)httpUrl.openConnection();
        //向文件所在服务器发送标识信息
         // 设置请求头User-Agent,模拟浏览器访问(避免被某些服务器拒绝)
        // 这里使用的是Edge浏览器的User-Agent标识
        httpUrlConnection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763");
        return httpUrlConnection;
    }
    //获取下载文件的文件名
    public static String getHttoFileNames(String url) {
        int index=url.lastIndexOf("/");// 查找URL中最后一个"/"的位置
        return url.substring(index+1); // 截取从最后一个"/"后面开始到URL末尾的子字符串(即文件名)
    }
}

3 文件下载器的基本功能

注意:无法直接在C:\ 根目录下创建文件,若是硬要在下面创建文件,应当以管理员的身份运行idea

package com.xie.core;
import com.xie.constant.Constant;
import com.xie.util.HttpUtils;
import java.io.*;
import java.net.HttpURLConnection;
//下载器
public class Downloader {
    public static void download(String url) {
        //获取文件名
        String httpFileNames = HttpUtils.getHttoFileNames(url);
        //文件下载路径
         httpFileNames = Constant.PATH + httpFileNames;
         //获取链接对象
        HttpURLConnection httpConnection=null;
        try {
          httpConnection = HttpUtils.getHttpConnection(url);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //拿下载文件的输入流,及对其异常进行处理
        try(
            InputStream input=httpConnection.getInputStream();//从 HTTP 连接获取的输入流(用于读取远程文件)
            BufferedInputStream bis=new BufferedInputStream(input);//缓冲输入流(提高读取效率)。
            FileOutputStream fos=new FileOutputStream(httpFileNames);//文件输出流(用于写入本地文件)。
            BufferedOutputStream bos=new BufferedOutputStream(fos);//缓冲输出流(提高写入效率)
            ){
            int len=-1;
            while((len=bis.read())!=-1){
                bos.write(len);
            }
        }catch (FileNotFoundException e){
            System.out.println(e.getMessage());
        }catch (IOException e){
            System.out.println("下载失败");
        }finally {
            //关闭连接对象
            if(httpConnection!=null){
                httpConnection.disconnect();
            }
        }
    }
}

4 日志工具类的编写

package com.xie.LogUtil;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

//日志工具类
public class LogUtil {
    public static void info(String msg , Object... args){
        print(msg,"-info-",args);
    }

public static void error(String msg , Object... args){
        print(msg,"-error-",args);
}

    private static void print(String msg , String level , Object... args){
        if(args.length > 0&&args != null){
            msg = String.format(msg.replace("{}","%s"), args);
            //将格式字符串中的%s替换为args内容。%s是格式说明符。
        }
        Thread name= Thread.currentThread();
        System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+" "+name+level+msg);
    }
}

5 ScheduledExecutorService的简介

5.1 延迟单次执行schedule()

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

  • 作用:延迟一定时间后执行任务(仅执行一次)。

  • 参数

    • command:要执行的任务(Runnable)。
    • delay:延迟时间。
    • unit:时间单位(如 TimeUnit.SECONDS)。

    示例代码:

    //获取对象
    ScheduledExecutorService s = Executors.newScheduledThreadPool(1);
    //延迟两秒后执行任务
    s.schedule(()->System.out.println(Thread.currentThread().getName()), 1, TimeUnit.SECONDS);
    //关闭
    s.shutdown();
    

    5.2 周期性执行(固定延迟)scheduleAtFixedRate()

    ScheduledFuture<?> scheduleAtFixedRate( Runnable command, long initialDelay, long period, TimeUnit unit )

  • 作用:以固定的 时间间隔 执行任务(从任务 开始时间 计算间隔)。

  • 特点

    • 如果任务执行时间超过 period,下次任务会 立即开始(无重叠延迟)。

    示例代码:

    //获取对象
    ScheduledExecutorService s = Executors.newScheduledThreadPool(1);
    s.scheduleAtFixedRate(()->{
        System.out.println(System.currentTimeMillis());;
        //模拟耗时操作
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    },2,3,TimeUnit.SECONDS);
    //任务会延迟2秒之后再执行,执行完后会等待3秒继续执行,
    // 但若是任务执行时间大于period的时间则之后不会再等待period的时间而是继续执行该任务
    

    5.3 周期性执行(固定间隔)scheduleWithFixedDelay()

    ScheduledFuture<?> scheduleAtFixedRate( Runnable command, long initialDelay, long period, TimeUnit unit )

    • 作用:以固定的 任务完成后的延迟 执行下一次任务。

    • 特点

      • 无论任务执行多久,两次任务之间始终间隔 delay

      示例代码:

      //获取对象
      ScheduledExecutorService s = Executors.newScheduledThreadPool(1);
      s.scheduleWithFixedDelay(()->{
          System.out.println(System.currentTimeMillis());;
          //模拟耗时操作
          try {
              TimeUnit.SECONDS.sleep(6);
          } catch (InterruptedException e) {
              throw new RuntimeException(e);
          }
      },2,3,TimeUnit.SECONDS);
      //任务会延迟2秒之后再执行,执行完后会等待3秒继续执行,
      // 但若是任务执行时间大于period的时间则之后会再等待period的时间而是继续执行该任务
      

      6 编写下载信息线程类

      ackage com.xie.core;
      
      import com.xie.constant.Constant;
      
      public class DownloadInfoThread implements Runnable {
          //下载的文件总大小,给他赋值的单位是字节
          private  long httpFileContentLength;
      
          //本地已下载文件的大小
          public double  downSize;
      
          //前一次下载的大小
          public double preSize;
      
          public DownloadInfoThread(long httpFileContentLength) {
              this.httpFileContentLength = httpFileContentLength;
          }
      
          @Override
          public void run() {
              //计算文件总大小 单位:mb
              String httpFileSize = String.format("%.2f", httpFileContentLength / Constant.MB);
      
              //计算每秒下载速度 kb
             int speed=(int)((downSize - preSize)/1024d);
             preSize=downSize;
      
             //剩余文件大小
              double remainSize=httpFileContentLength-downSize;
      
              //计算剩余时间
              String remainTime=String.format(".1f",remainSize=remainSize/1024d/speed);
      
              if ("Infinity".equals(remainTime)) {
                  remainTime="-";
              }
      
              //已下载大小
             String currentFileSize= String.format("%.2f",downSize/Constant.MB);
      
              String downInfo=String.format("已下载%smb,速度 %skb/s,剩余时间 %ss",
                      currentFileSize,httpFileSize,remainTime);
      
              System.out.print("\r");
              System.out.print(downInfo);
          }
      }
      

      7 文件切割功能的实现

      7.1 分块下载工具方法的实现

      public static HttpURLConnection getHttpURLConnection(String url,long startPos,long endPos) throws IOException {
              HttpURLConnection httpURLConnection = getHttpUrlConnection(url);
              httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos);
              if (endPos!=0){
                  httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos);
              }else {
                  httpURLConnection.setRequestProperty("Range","bytes=" + startPos);
              }
              return httpURLConnection;
          }
      

8 分块任务类

package com.xie.core;

import com.xie.constant.Constant;
import com.xie.util.HttpUtils;
import com.xie.util.LogUtil;

import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.util.concurrent.Callable;
//分块下载任务
public class DownloaderTask implements Callable<Boolean> {

    private String url;

    private long startPos;

    private long endPos;

    //标识是哪一部分
    private int part;

    public DownloaderTask(String url, long startPos, long endPos, int part) {
        this.url = url;
        this.startPos = startPos;
        this.endPos = endPos;
        this.part = part;
    }

    @Override
    public Boolean call() throws Exception {
        //获取文件名
        String httpFileNames = HttpUtils.getHttoFileNames(url);
        //设置分块的文件名
        httpFileNames =httpFileNames +".temp"+part;
        //下载路径
        httpFileNames= Constant.PATH+httpFileNames;
        //获取分块下载的链接
        HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);
        try(InputStream input=httpURLConnection.getInputStream();
            BufferedInputStream bis=new BufferedInputStream(input);
            RandomAccessFile accessFile=new RandomAccessFile(httpFileNames,"rw");
        ){
            byte[] buffer = new byte[Constant.BYTE_SIZE];
            int length=-1;
            while((length=bis.read(buffer))!=-1){
                accessFile.write(buffer,0,length);
            }

        }catch (FileNotFoundException e){
            LogUtil.error("下载文件不存在{}",url);
            return false;
        }catch (Exception e){
            LogUtil.error("下载出现异常");
            return false;
        }finally {
            httpURLConnection.disconnect();
        }
        return true;
    }
}

9多线程下载器源码

9.1 constant包下的Constant类

package com.xie.constant;
//常量类
public class Constant {
    public static final String PATH="C:\\";

    public static final double MB=1024*1024;

    public static final int BYTE_SIZE=1024*100;
}

9.2 core包下的类

9.2.1 Downloader类
package com.xie.core;
import com.xie.util.LogUtil;
import com.xie.constant.Constant;
import com.xie.util.FileUtils;
import com.xie.util.HttpUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//下载器
public class Downloader {
    public ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public void download(String url) {
        //获取文件名
        String httpFileNames = HttpUtils.getHttoFileNames(url);
        //文件下载路径
         httpFileNames = Constant.PATH + httpFileNames;

         //获取本地文件的大小
        long locaFileLength= FileUtils.getFileContentLength(httpFileNames);


         //获取链接对象
        HttpURLConnection httpConnection=null;
        DownloadInfoThread downloadInfoThread =null;
        try {
          httpConnection = HttpUtils.getHttpUrlConnection(url);
          //获取下载文件总大小
            int contentLength = httpConnection.getContentLength();

          //判断文件是否已经下载过
          if (locaFileLength>=contentLength) {
              LogUtil.info("{}已下载完毕,无需重新下载", httpFileNames);
          }

          //创建获取下载信息的任务对象
           downloadInfoThread = new DownloadInfoThread();
            //将任务交给线程执行,每隔一秒执行一次
            scheduler.scheduleAtFixedRate(downloadInfoThread,1,1, TimeUnit.MINUTES);


        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("\r");
            System.out.println("下载完成");
            //关闭连接对象
            if(httpConnection!=null){
                httpConnection.disconnect();
            }

            //关闭
            scheduler.shutdownNow();
        }

    }

}
9.2.2 DownloaderTask
package com.xie.core;

import com.xie.constant.Constant;
import com.xie.util.HttpUtils;
import com.xie.util.LogUtil;

import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.util.concurrent.Callable;
//分块下载任务
public class DownloaderTask implements Callable<Boolean> {

    private String url;

    private long startPos;

    private long endPos;

    //标识是哪一部分
    private int part;

    public DownloaderTask(String url, long startPos, long endPos, int part) {
        this.url = url;
        this.startPos = startPos;
        this.endPos = endPos;
        this.part = part;
    }

    @Override
    public Boolean call() throws Exception {
        //获取文件名
        String httpFileNames = HttpUtils.getHttoFileNames(url);
        //设置分块的文件名
        httpFileNames =httpFileNames +".temp"+part;
        //下载路径
        httpFileNames= Constant.PATH+httpFileNames;
        //获取分块下载的链接
        HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);
        try(InputStream input=httpURLConnection.getInputStream();
            BufferedInputStream bis=new BufferedInputStream(input);
            RandomAccessFile accessFile=new RandomAccessFile(httpFileNames,"rw");
        ){
            byte[] buffer = new byte[Constant.BYTE_SIZE];
            int length=-1;
            while((length=bis.read(buffer))!=-1){
                accessFile.write(buffer,0,length);
            }

        }catch (FileNotFoundException e){
            LogUtil.error("下载文件不存在{}",url);
            return false;
        }catch (Exception e){
            LogUtil.error("下载出现异常");
            return false;
        }finally {
            httpURLConnection.disconnect();
        }
        return true;
    }
}
9.2.3 DownloadInfoThread
package com.xie.core;

import com.xie.constant.Constant;

public class DownloadInfoThread implements Runnable {
    //下载的文件总大小,给他赋值的单位是字节
    private  long httpFileContentLength;

    //本地已下载文件的大小
    public double  downSize;

    //前一次下载的大小
    public double preSize;

    public DownloadInfoThread() {
        this.httpFileContentLength = httpFileContentLength;
    }

    @Override
    public void run() {
        //计算文件总大小 单位:mb
        String httpFileSize = String.format("%.2f", httpFileContentLength / Constant.MB);

        //计算每秒下载速度 kb
       int speed=(int)((downSize - preSize)/1024d);
       preSize=downSize;

       //剩余文件大小
        double remainSize=httpFileContentLength-downSize;

        //计算剩余时间
        String remainTime=String.format(".1f",remainSize=remainSize/1024d/speed);

        if ("Infinity".equals(remainTime)) {
            remainTime="-";
        }

        //已下载大小
       String currentFileSize= String.format("%.2f",downSize/Constant.MB);

        String downInfo=String.format("已下载%smb,速度 %skb/s,剩余时间 %ss",
                currentFileSize,httpFileSize,remainTime);

        System.out.print("\r");
        System.out.print(downInfo);
    }
}

9.3 util包下的类

9.3.1 FileUtils
package com.xie.util;

import java.io.File;

public class FileUtils {
    //获取本地文件的大小
    public static long getFileContentLength(String path){
        File file = new File(path);
        return file.exists() && file.isFile() ? file.length() : 0;

    }
}
9.3.2 HttpUtils
package com.xie.util;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
 * 获取HttpURLConnection链接对象
 * url文件地址
 * */
public class HttpUtils {
    public static HttpURLConnection getHttpUrlConnection(String url) throws IOException {
        URL httpUrl = new URL(url);
        HttpURLConnection httpUrlConnection =(HttpURLConnection)httpUrl.openConnection();
        //向文件所在服务器发送标识信息
        httpUrlConnection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763");
        return httpUrlConnection;
    }
    //获取下载文件的文件名
    public static String getHttoFileNames(String url) {
        int index=url.lastIndexOf("/");
        return url.substring(index+1);
    }

    public static HttpURLConnection getHttpURLConnection(String url,long startPos,long endPos) throws IOException {
        HttpURLConnection httpURLConnection = getHttpUrlConnection(url);
        httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos);
        if (endPos!=0){
            httpURLConnection.setRequestProperty("Range","bytes=" + startPos + "-" + endPos);
        }else {
            httpURLConnection.setRequestProperty("Range","bytes=" + startPos);
        }
        return httpURLConnection;
    }


}
9.3.3
package com.xie.util;



import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

//日志工具类
public class LogUtil {
    public static void info(String msg , Object... args){
        print(msg,"-info-",args);
    }

    public static void error(String msg , Object... args){
        print(msg,"-error-",args);
}

    private static void print(String msg , String level , Object... args){
        if(args.length > 0&&args != null){
            msg = String.format(msg.replace("{}","%s"), args);
            //将格式字符串中的%s替换为args内容。%s是格式说明符。
        }
        Thread name= Thread.currentThread();
        System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+" "+name+level+msg);
    }
}

9.4 主类

package com.xie;
import com.xie.util.LogUtil;
import com.xie.core.Downloader;

import java.util.Scanner;
//主类
public class Main {
    public static void main(String[] args) {
        //下载地址
        String url=null;
        if(args ==null || args.length==0){
            for (; ;){
                LogUtil.info("请输入下载地址");
                Scanner scanner = new Scanner(System.in);
                url= scanner.next();
                if (url!=null ){
                    break;
                }
            }
        }else {
            url = args[0];
        }
        Downloader downloader = new Downloader();
        downloader.download(url);
    }
}

```java
package com.xie;
import com.xie.util.LogUtil;
import com.xie.core.Downloader;

import java.util.Scanner;
//主类
public class Main {
    public static void main(String[] args) {
        //下载地址
        String url=null;
        if(args ==null || args.length==0){
            for (; ;){
                LogUtil.info("请输入下载地址");
                Scanner scanner = new Scanner(System.in);
                url= scanner.next();
                if (url!=null ){
                    break;
                }
            }
        }else {
            url = args[0];
        }
        Downloader downloader = new Downloader();
        downloader.download(url);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值