多线程下载器(Java版本)

该程序是一个Java编写的下载工具,能读取源文件中的URL列表,通过多线程自动下载网络资源到本地,并在下载过程中处理错误,自定义下载目录、任务数量及文件名。下载完成后,会在控制台显示文件的存储路径和大小。
摘要由CSDN通过智能技术生成

一、需求分析

1.通过读取源文件多线程自动下载所有网络资源到本地硬盘
2.遇到下载故障在控制台打印错误信息
3.允许自定义源文件的地址,保存下载文件的目录要自动创建
4.允许自定义同时下载的任务数量,不指定默认开启10个下载任务
5.下载文件的文件名要与网址包含文件名保持一 致
6.下载成功后在控制台输出存储路径与文件尺寸


二、实现效果

控制台运行效果

 通过程序下载的内容

 

三、实现代码

package com.imooc.downloader;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Downloader {
    private Integer threadNum = 10;
    /**
     * 下载单个文件保存到本地
     * @param source 原图片的网址
     * @param targetDir 目标目录,要确保已存在
     */
    public void download(String source , String targetDir){
        InputStream is = null;
        OutputStream os = null;
        try{
            //https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
            String fileName = source.substring(source.lastIndexOf("/") + 1);
            File targetFile = new File(targetDir + "/" + fileName);
            if(!targetFile.exists()){
                targetFile.createNewFile();
            }
            URL url = new URL(source);
            URLConnection connection = url.openConnection();
            is = connection.getInputStream();
            os = new FileOutputStream(targetFile);
            byte[] bs = new byte[1024];
            int len = 0;
            while((len = is.read(bs)) != -1){
                os.write(bs,0,len);
            }
            System.out.println("[INFO]图片下载完毕:" + source + "\n\t  ->" + targetFile.getPath() + "(" + Math.floor(targetFile.length() / 1024) + "kb)");
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try{
                if(os != null){
                    os.close();
                }
                if(is != null){
                    is.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 从指定文件读取下载地址,批量下载网络资源
     * @param targetDir 下载文件的存储目录
     * @param downloadTxt download.txt完整路径
     */
    public void multiDownloadFromFile(String targetDir ,String downloadTxt){
        File dir = new File(targetDir);
        //新建目标目录
        if(!dir.exists()){
            dir.mkdirs();
            System.out.println("[INFO]发现下载目录[" + dir.getPath() + "]不存在,已自动创建");
        }
        //读取download.txt存入resources集合
        List<String> resources = new ArrayList<>();
        BufferedReader reader = null;
        ExecutorService threadPool = null;
        try {
            reader = new BufferedReader(new FileReader(downloadTxt));
            String line = null;
            //读取每一行网址,存入resources集合
            while((line = reader.readLine()) != null){
                resources.add(line);
                //System.out.println(line);
            }
            //利用线程池,同时开启{threadNum}个线程批量下载图片并保存到本地
            threadPool = Executors.newFixedThreadPool(this.threadNum);
			//this在不同的类指向的对象是不一样的,如果要在不同的类内侧使用其他对象的this,可以用that指向别的对象
            Downloader that = this;
            for(String res:resources){
                threadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        that.download(res,targetDir);
                    }
                });
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            //执行完毕,关闭线程池
            if(threadPool != null){
                threadPool.shutdown();
            }
            if(reader != null){
                try {
                    //关闭字节输入流
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 开始多线程下载
     * @param propDir config.properties所在目录
     */
    public void start(String propDir){
        //指定config.properties完整路径
        File propFile = new File(propDir + "\\config.properties");
        //实例化Properties属性类
        Properties properties = new Properties();
        Reader reader = null;
        try {
            //实例化FileReader对象
            reader = new FileReader(propFile);
            //properties对象读取配置文件
            properties.load(reader);
            //通过getProperty方法得到对应的选项值
            String threadNum = properties.getProperty("thread-num");
            this.threadNum = Integer.parseInt(threadNum);
            String targetDir = properties.getProperty("target-dir");
            //打印输出
            //System.out.println(threadNum);
            //System.out.println(targetDir);
            //读取download.txt进行多线程下载
            this.multiDownloadFromFile(targetDir,propDir+"\\download.txt");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(reader != null){
                try {
                    //关闭流
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {

        Downloader downloader = new Downloader();
        downloader.start("D:\\code\\downloader\\src");
        //downloader.download("https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg","d:/a/d");
    }
}

四、目录结构

在src目录下一定要有配置文件config.properties和下载内容的地址download.txt,如下图,两个文件一定要在src同一目录下

 配置文件config.properties的内容

一个是线程数量,一个是文件存放地址

thread-num=10  
target-dir=d:/a/b 

download.txt文件存放内容

https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11572548.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11593467.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-11631922.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12203460.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/pexels-photo-12240136.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10092819.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10519162.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-10977780.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-11311195.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-11954484.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-12072057.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-12072057.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-4542338.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-6323680.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-6873171.jpeg
https://manongbiji.oss-cn-beijing.aliyuncs.com/imooc/pexels/s/pexels-photo-7409835.jpeg

1.得到服务下载文件的大小,然后在本地设置一个临时文件和服务端文件大小一致 a)获得访问网络地址 b)通过URL对象的openConnection()方法打开连接,返回一个连接对象 c)设置请求头 i.setRequestMethod ii.setConnectTimeout iii.setReadTimeout d)判断是否响应成功 e)获取文件长度(getContentLength()) f)随机访问文件的读取与写入RandomAccessFile(file, mode) g)设置临时文件与服务文件大小一致(setLength()) h)关闭临时文件 2.计算出每个线程下载的大小(开始位置,结束位置) a)计算出每个线程下载的大小 b)for循环,计算出每个线程的开始、结束位置 c)最后一个线程处理 3.每创建好一次就要开启线程下载 a)构造方法 b)通过URL对象的openConnection()方法打开连接,返回一个连接对象 c)设置请求头 i.setRequestMethod ii.setConnectTimeout d)判断是否响应成功(206) e)获取每个线程返回的流对象 f)随机访问文件的读取与写入RandomAccessFile(file, mode) g)指定开始位置 h)循环读取 i.保存每个线程下载位置 ii.记录每次下载位置 iii.关闭临时记录位置文件 iv.随机本地文件写入 v.记录已下载大小 i)关闭临时文件 j)关闭输入流 4.为了杀死线程还能继续下载的情况下,从本地文件上读取已经下载文件的开始位置 a)创建保存记录结束位置的文件 b)读取文件 c)将流转换为字符 d)获取记录位置 e)把记录位置赋给开始位置 5.当你的n个线程都下载完毕的时候我进行删除记录下载位置的缓存文件 a)线程下载完就减去 b)当没有正在运行的线程时切文件存在时删除文件
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大臣呀~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值