用JAVA写一个下载器第2集

news2024/12/23 6:20:38

文章目录

  • 一、开发环境及工具
  • 二、包名概览
  • 三、项目结构
  • 四、使用步骤
    • 1.编写代码
      • Constant.java:
      • Downloader.java
      • DownloaderTask.java
      • DownloadInfoThread.java
      • FileUtils.java
      • HttpUtils.java
      • LogUtils.java
      • Main.java
    • 2.运行程序
  • 总结


一、开发环境及工具

开发环境及工具
IDEA
JDK8
UTF-8

二、包名概览

constant:存放常量类的包
core:存放了下载器核心类的包
util:存放工具类的包
Main:主类

三、项目结构

在这里插入图片描述
Main是主类,主函数在其中。

四、使用步骤

1.编写代码

Constant.java:

package com.downloader.constant;

/*常量类*/
public class Constant {
    public static final String PATH="D:\\Java_dm\\TestDownloaderPath\\";


    public static final double MB=1024d*1024d;


    public static final int BYTE_SIZE=1024*100;


    public static final int THREAD_NUM=5;
}

Downloader.java

package com.downloader.core;

import com.downloader.constant.Constant;
import com.downloader.util.FileUtils;
import com.downloader.util.HttpUtils;
import com.downloader.util.LogUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.concurrent.*;

/*下载器*/
public class Downloader {

    public ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(1);
    public ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor(Constant.THREAD_NUM,Constant.THREAD_NUM,0,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5));
    private CountDownLatch countDownLatch=new CountDownLatch(Constant.THREAD_NUM);
    public void download(String url){
        //获取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        //文件保存路径
        httpFileName= Constant.PATH+httpFileName;

        /*获取本地文件的大小*/
        long localFileLength = FileUtils.getFileContentLength(httpFileName);

        //获取连接对象
        HttpURLConnection httpURLConnection =null;
        DownloadInfoThread downloadInfoThread=null;
        try {
            httpURLConnection = HttpUtils.getHttpURLConnection(url);
            int contentLength = httpURLConnection.getContentLength();

            /*判断文件是否已经下载过*/
            if (localFileLength>=contentLength){
                LogUtils.info("{}已经下载完毕,无需重新下载",httpFileName);
                return;
            }

            /*创建获取下载信息的任务对象*/
            downloadInfoThread = new DownloadInfoThread(contentLength);
            scheduledExecutorService.scheduleAtFixedRate(downloadInfoThread,1,1, TimeUnit.SECONDS);

            //切分任务
            ArrayList<Future> list = new ArrayList<>();
            spilt(url,list);

            countDownLatch.await();//等待所有线程结束

            /*合并文件*/
           if(merge(httpFileName)){
               clearTemp(httpFileName);//清楚文件
           }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.print("\r");
            System.out.print("下载完成");
            if (httpURLConnection!=null){
                httpURLConnection.disconnect();//关闭连接
            }

            scheduledExecutorService.shutdownNow();//关闭线程
            poolExecutor.shutdown();//关闭线程池
        }

    }


    public void spilt(String url, ArrayList<Future> futureList){
        try {
            long contentLength = HttpUtils.getHttpFileContentLength(url);
            long size = contentLength / Constant.THREAD_NUM;
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                long startPos=i*size;
                long endPos;
                if(i==Constant.THREAD_NUM-1){
                    endPos=0;
                }else {
                    endPos=startPos+size-1;
                }
                DownloaderTask downloaderTask = new DownloaderTask(url, startPos, endPos,i,countDownLatch);
                /*将任务提交到线程池中*/
                Future<Boolean> future = poolExecutor.submit(downloaderTask);
                futureList.add(future);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean merge(String fileName){
        System.out.print("\n");
        LogUtils.info("开始合并文件{}",fileName);
        byte[] buffer=new byte[Constant.BYTE_SIZE];
        int len=-1;
        try(RandomAccessFile accessFile=new RandomAccessFile(fileName,"rw")){
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                try(BufferedInputStream bis=new BufferedInputStream(new FileInputStream(fileName+".temp"+i))){
                    while ((len=bis.read(buffer))!=-1){
                        accessFile.write(buffer,0,len);
                    }
                }
            }
            LogUtils.info("文件合并完毕{}",fileName);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }


    public boolean clearTemp(String fileName){
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            File file=new File(fileName+".temp"+i);
            file.delete();
        }
        return true;
    }




}

DownloaderTask.java

package com.downloader.core;

import com.downloader.constant.Constant;
import com.downloader.util.HttpUtils;
import com.downloader.util.LogUtils;

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;
import java.util.concurrent.CountDownLatch;

public class DownloaderTask implements Callable<Boolean> {
    /*分块下载任务*/
    private String url;
    private long startPos;///起始位置
    private long endPos;//结束位置
    private int part;//下载的是哪一个部分
    private CountDownLatch countDownLatch;

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

    @Override
    public Boolean call() throws Exception {
        String httpFileName = HttpUtils.getHttpFileName(url);
        httpFileName=httpFileName+".temp"+part;//分块的文件名
        httpFileName= Constant.PATH+httpFileName;//下载路径

        /*获取分块下载的连接*/
        HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);

        try(
                InputStream inputStream=httpURLConnection.getInputStream();
                BufferedInputStream bis=new BufferedInputStream(inputStream);
                RandomAccessFile accessFile= new RandomAccessFile(httpFileName,"rw");
        ){
            byte[] buffer = new byte[Constant.BYTE_SIZE];
            int len=-1;
            while ((len=bis.read(buffer))!=-1){
                DownloadInfoThread.downSize.add(len);
                accessFile.write(buffer,0,len);
            }
        }catch (FileNotFoundException e){
            LogUtils.error("下载文件不存在{}",url);
            return false;
        }catch (Exception e){
            LogUtils.error("下载出现异常");
            return false;
        }finally {
            httpURLConnection.disconnect();
            countDownLatch.countDown();//减1操作,等待清零
        }
        return true;
    }
}

DownloadInfoThread.java

package com.downloader.core;

import com.downloader.constant.Constant;

import java.util.concurrent.atomic.LongAdder;

public class DownloadInfoThread implements Runnable{

    /*下载文件总大小   */
    private long httpFileContentLength;

    /*本地已下载文件的大小*/
    public static LongAdder finishedSize=new LongAdder();

    /*本次累计下载的大小*/
    public static volatile LongAdder downSize=new LongAdder();

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

    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.doubleValue() - prevSize) / 1024d);
        prevSize=downSize.doubleValue();
        /*剩余文件的大小*/
        double remainSize = httpFileContentLength - finishedSize.doubleValue() - downSize.doubleValue();
        /*计算剩余时间*/
        String remainTime = String.format("%.1f", remainSize / 1024d / speed);

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

        /*已下载文件大小*/
        String currentFileSize = String.format("%.2f", (downSize.doubleValue() - finishedSize.doubleValue()) / Constant.MB);

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

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

    }
}

FileUtils.java

package com.downloader.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;
    }

}

HttpUtils.java

package com.downloader.util;

import java.io.IOException;
import java.net.*;

/*http相关工具类*/
public class HttpUtils {

    public static long getHttpFileContentLength(String url) throws IOException {
        int contentLength;
        HttpURLConnection httpURLConnection=null;
        try {
            httpURLConnection = getHttpURLConnection(url);
            contentLength = httpURLConnection.getContentLength();
        } finally {
            if(httpURLConnection!=null){
                httpURLConnection.disconnect();
            }
        }
        return contentLength;
    }

    /*分块下载方法*/
    public static HttpURLConnection getHttpURLConnection(String url,long startPos,long endPos) throws IOException {
        HttpURLConnection httpURLConnection=getHttpURLConnection(url);
        LogUtils.info("下载的区间是:{}--{}",startPos,endPos);
        if(endPos!=0){
            httpURLConnection.setRequestProperty("RANGE","bytes="+startPos+"-"+endPos);
        }else {
            httpURLConnection.setRequestProperty("RANGE","bytes="+startPos+"-");
        }
        return httpURLConnection;
    }



    /*获取HttpURLConnection连接对象*/
    public static HttpURLConnection getHttpURLConnection(String url) throws IOException {
        URL httpUrl = new URL(url);
        HttpURLConnection urlConnection = (HttpURLConnection)httpUrl.openConnection();
        //向文件所在的服务器发送标识信息
        urlConnection.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (HTML, like Gecko) Chrome/14.0.835.163 Safari/535.1");
        return urlConnection;
    }

    /*获取下载文件的名称*/
    public static String getHttpFileName(String url){
        int indexOf = url.lastIndexOf("/");
        return url.substring(indexOf+1);
    }
}

LogUtils.java

package com.downloader.util;

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

/*日志工具类*/
public class LogUtils {

     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!=null&&args.length>0){
               msg=String.format(msg.replace("{}","%s"),args);
          }
          String name = Thread.currentThread().getName();
          System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("hh:mm:ss"))+""+name+level+msg);

     }
}

Main.java

package com.downloader;

import com.downloader.core.Downloader;
import com.downloader.util.LogUtils;

import java.util.Scanner;

public class Main {
    /*主类,程序的入口*/
    public static void main(String[] args) {
        String url=null;//用来放下载连接的地址。
        if(args!=null && args.length!=0){//判断主函数的传入字符数组是否为空
            url=args[0];//如果不为空,对url进行赋值arg[0]
        }else {
            while (true){//主函数传入字符数组为空,进入while循环
//                System.out.println("请输入下载文件的地址。");
                LogUtils.info("请输入下载文件的地址");
                Scanner scanner=new Scanner(System.in);//在控制台中获取输入的信息。
                url=scanner.next();//对url进行赋值,为控制台中输入的信息。
                if(url!=null){//如果url不为空,结束while循环。
                    break;
                }
            }
        }
        Downloader downloader = new Downloader();
        downloader.download(url);
    }
}

2.运行程序

在这里插入图片描述

链接地址:
https://dldir1.qq.com/qqfile/qq/PCQQ9.7.1/QQ9.7.1.28940.exe
在这里插入图片描述


总结

这是一个用多线程下载一个文件的程序,主要是对多线程的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/683265.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如果开发说这不是Bug,你会怎么处理?

在项目过程中&#xff0c;如果开发说这个不是Bug&#xff0c;你的第一反应是什么&#xff1f; 不同的人有不同的处理方式&#xff0c;也许是如下几点&#xff1a;相信开发说的&#xff0c;开发说什么就是什么&#xff0c;问题关闭&#xff1b;自己不能决定&#xff0c;啥都上升…

GP232RNL——USB到UART桥接控制器

GP232RNL是一款高度集成的USB到UART桥接控制器&#xff0c;提供了一种简单的解决方案&#xff0c;可以使用最少的元器件和PCB空间&#xff0c;将RS232接口转换为USB接口。GP232RNL包括一个USB 2.0全速功能控制器、USB收发器、振荡器、EEPROM和带有完整的调制解调器控制信号的异…

日撸java三百行day69-70

文章目录 说明day69-70 矩阵分界1.基于矩阵分解的推荐系统&#xff08;Funk-SVD算法&#xff09;2.随机梯度下降&#xff08;SGD&#xff09;2.1 导数2.2 偏导数2.3 方向导数2.4 梯度2.5 随机梯度下降&#xff0c;与损失函数之间的关系 3.代码理解3.1 train() 方法3.2 mae方法&…

神经网络原理(2)

斯坦福大学的印度学生、机器学习爱好者 PararthShah 在2012年12月22日的使用买芒果的例子解释了神经网络&#xff0c;简单来说就是&#xff1a;如果你需要选芒果&#xff0c;但不知道什么样的芒果最好吃&#xff0c;一个简单粗暴的方法是尝遍所有的芒果&#xff0c;然后总结出个…

窗口函数之-前后函数(lag/lead)

窗口函数之-前后函数 应用&#xff1a;求同比增长、环比增长 lead(expression,n):返回当前行的后n行 > shift(-n) 数据超前n阶&#xff0c;与之对齐的就是后n行的数据lag(expression,n):返回当前行的前n行> shift(n)数据滞后n阶&#xff0c;与之对齐的就是前n行的数据 …

人工智能轨道交通行业周刊-第49期(2023.6.12-6.25)

本期关键词&#xff1a;设备智能维修、故障诊断、无人机巡查、车站联锁、LangChain、腾讯大模型 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMe…

高效进行接口测试,简单易懂!

目录 前言 正文 1.Api文档导入 2.后端接口测试 3.mock数据 4.测试集接口自动化 总结 前言 日常测试过程中&#xff0c;常常需要多种工具来接力完成自己的接口测试任务。 比如说&#xff0c; 使用swagger查看接口文档&#xff0c; 使用mock编造接口数据对前端页面做测试…

面对高速PCB设计,你是否也有这些疑问?

在现代电子产品设计中&#xff0c;高速PCB设计是很重要的组成部分&#xff0c;然而由于高速信号的特殊性和复杂性&#xff0c;很多电子工程师在进行高速PCB设计时难免面临各种挑战及问题&#xff0c;那么面对这些挑战及问题&#xff0c;该如何解决&#xff1f; 1、添加测试点是…

管线业务模块实现

文章目录 1 .配电线路轨迹图2 &#xff09;单线图存储功能3. 设备接线图1 &#xff09;剖面绘制 电力管线业务模块是系统的电力业务功能&#xff0c;主要维护电气设备的电力连接关系以及电 缆在GIS 地图和工井内部的位置和走向。管线业务模块由配电线路的轨迹图、单线图、接 线…

介绍智能照明系统在绿色建筑中的应用与产品选型

【摘要】&#xff1a;智能照明系统应用在智能建筑中不仅能营造出舒适的生活、工作环境以及现代化的管理方式而且要具有一定的节能效果。给出了智能照明和传统照明系统的比较并分析了智能照明系统的节能。 【关键字】&#xff1a;智能建筑&#xff1b;智能照明&#xff1b;节能…

蓝库云:企业绩效管理对公司成长的重要性,被很多企业主所忽略

什么是绩效管理 绩效管理是一种管理方法&#xff0c;通过衡量和评估员工的工作绩效&#xff0c;来提高企业的效率和效益。它涉及一个系统性的过程&#xff0c;包括设定、评估和反馈员工目标&#xff0c;制定和实施成长计划和发展计划&#xff0c;以及与员工进行持续的沟通和反…

MacOS 中 ARM64 汇编 ldr =address 伪指令导致运行时崩溃的原因及解决

0. 概览 我们知道在 MacOS 的 as 汇编器中有一条 ldr 伪指令&#xff0c;使用它我们可以非常方便的将立即数加载到寄存器中。 不过&#xff0c;当 ldr 的源操作数是一个标签&#xff08;Label&#xff09;时&#xff0c;就会导致在运行时发生崩溃&#xff1a; 如上图所示&…

179_自动生成 千万级 Power BI 示例数据

179_自动生成 千万级 Power BI 示例数据 在早一些是时候&#xff0c;我曾写过一个示例数据《赠送300家门店260亿销售额的零售企业Power BI实战示例数据》&#xff0c;本次我们对该示例数据做了一些调整。 一、更新内容 针对有一些朋友不会使用 vba 模块&#xff0c;我们增加了…

RHEL CentOS Debian Ubuntu 如何刷新 DNS 缓存

RHEL CentOS Debian Ubuntu 如何刷新 DNS 缓存 全文&#xff1a;如何刷新 DNS 缓存 (macOS, Linux, Windows) Unix Linux Windows 如何刷新 DNS 缓存 (macOS, FreeBSD, RHEL, CentOS, Debian, Ubuntu, Windows) 请访问原文链接&#xff1a;https://sysin.org/blog/how-to-fl…

【YOLO】目标识别模型的导出和opencv部署

文章目录 0 前期教程1 什么是模型部署2 怎么部署 0 前期教程 【YOLO】朴实无华的yolov5环境配置 【YOLO】yolov5训练自己的数据集 1 什么是模型部署 前期教程当中&#xff0c;介绍了yolov5环境的搭建以及如何利用yolov5进行模型训练和测试&#xff0c;虽然能够实现图片或视频…

语法篇·JSP基础

一、初识JSP 1.1简介 JSP(Java Server Pages),其根本是一个简化的Servlet设计&#xff0c;它实现了在Java中使用HTML标签。JSP是一种动态网页技术标准&#xff0c;也是JavaEE的标准。JSP和Servlet一样&#xff0c;是在服务器端执行的。JSP是在Servlet技术发展之后为了让开发者…

苹果公司开发者账号申请流程

目录 一、注册 Apple ID 账号二、Apple Developer 登录三、申请公司邓白氏编码四、下载 Apple Developer app五、审核六、缴费七、发票 一、注册 Apple ID 账号 注册网址&#xff1a;https://appleid.apple.com/account 二、Apple Developer 登录 登录网址&#xff1a;http…

汽车EBSE测试流程分析(二):关于优势和挑战的案例分析

EBSE专题连载共分为“五个”篇章。此文为该连载系列的“第二”篇章&#xff0c;在之前的“篇章&#xff08;一&#xff09;”中已经阐述了汽车软件工程的特点&#xff0c;以及使用混合方法设计的分阶段EBSE测试过程&#xff0c;并提出问题。接下来&#xff0c;我们将具体分析EB…

抖音矩阵系统源码开发指南

抖音矩阵系统是一个大规模的分布式系统&#xff0c;它可以处理数百万级别的并发请求。要开发和部署抖音矩阵系统源代码 您需要遵循以下步骤&#xff1a; 下载和安装必要的软件依赖项&#xff1a;抖音矩阵系统源代码需要使用Java和Scala编程语言&#xff0c;因此您需要下载和安…

2023年程序员工资中位数增长10%?开发者最常用的语言竟然是……

在调研了全球超过 90000 名开发者之后&#xff0c;程序员社区 Stack Overflow 重磅发布了《2023 Developer Survey》调查报告。在本次报告中&#xff0c;Stack Overflow 从工具、编码、工作、社区等维度展开&#xff0c;同时深入研究了 AI/ML 技术&#xff0c;并解析开发者如何…