用java实现多线程下载器

多线程下载器

开发语言

  • JDK1.8

功能介绍

  • 通过读取源文件(download.txt,其中可包含多个网址,每个网址独占一行)多线程下载网络资源到本地硬盘
  • 遇到下载故障在控制台打印错误消息
  • 用户可以自定义源文件地址,提示用户创建保存下载文件的目录
  • 用户可以定义同时下载的任务数量,默认开启10个下载任务
  • 下载后的文件名为网址包含的文件名加上时间戳(防止文件重名)
  • 下载成功后在控制台输出存储路径与文件尺寸

使用说明

  • 在download.txt文件里输入要下载资源网址,每个网址独占一行
  • download.txt文件的位置默认在项目的conf目录下
  • 在config.properties文件里定义thread-num为同时选择的线程数
  • 在config.properties文件里定义target-dir为下载文件保存的目录(默认为G:/foo),建议改为D:/foo
  • config.properties文件的位置默认在项目的conf目录下
  • idea目录下的代码实现.xmind文件请用xmind软件打开
  • 运行Main.java开始下载

目录结构

目录结构

Downloader.java

package src.com.yuan;

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;


    /**
     * 读取配置文件,完成初始化
     *
     * @param propDir 配置文件存放的目录
     */
    public void start(String propDir) {
        //读取配置文件
        File propFile = new File(propDir + "/config.properties");
        Properties properties = new Properties();
        Reader reader = null;

        try {
            reader = new FileReader(propFile);
            properties.load(reader);
            String threadNum = properties.getProperty("thread-num");
            String targetDir = properties.getProperty("target-dir");
            this.threadNum = Integer.parseInt(threadNum);
            this.superDownload(propDir + "/download.txt", targetDir);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }


    /**
     * 多线程下载文件保存到本地
     *
     * @param downloadTxt 下载文件网址储存目录
     * @param targetDir   下载文件保存的目录,要确保已存在
     */
    public void superDownload(String downloadTxt, String targetDir) {
        File dir = new File(targetDir);
        if (!dir.exists()) {
            dir.mkdirs();
            System.out.println("[INFO]发现下载目录[" + dir.getPath() + "]不存在,已自动创建");
        }

        List<String> resource = new ArrayList<>();

        BufferedReader bfreader = null;
        ExecutorService threadPool = null;
        try {
            bfreader = new BufferedReader(new FileReader(downloadTxt));
            String line = null;
            while ((line = bfreader.readLine()) != null) {
                resource.add(line);
                System.out.println(line);
            }
            threadPool = Executors.newFixedThreadPool(this.threadNum);
            Downloader that = this;//将当前对象传递给匿名类
            for (String item:resource) {
                threadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        that.download(item,targetDir);
                    }
                });
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (bfreader != null) {
                try {
                    bfreader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            if (threadPool != null) {
                threadPool.shutdown();
            }
        }


    }


    /**
     * 单线程下载文件保存到本地
     *
     * @param source    源文件的网址
     * @param targetDir 下载文件保存的目录,要确保已存在
     */
    public void download(String source, String targetDir) {
        InputStream inputStream = null;//输入流
        OutputStream outputStream = null;//输出流

        try {

            //从网址中截取文件名 即最后一个`/`后的内容
            String fileName = source.substring(source.lastIndexOf("/") + 1);
            //获取当前时间戳
            long timestamp = System.currentTimeMillis();
            //将时间戳转换为字符串
            String timestampStr = String.valueOf(timestamp);

            //加上时间戳得到唯一的文件名
            fileName = timestampStr + "-" + fileName;

            //创建目标文件对象
            File targetFile = new File(targetDir + "/" + fileName);
            if (!targetFile.exists()) {
                targetFile.createNewFile();//如果文件不存在则新建文件
            }

            //获取网络资源输入流对象
            URL url = new URL(source);
            URLConnection connection = url.openConnection();
            inputStream = connection.getInputStream();

            //获取文件输出流对象
            outputStream = new FileOutputStream(targetFile);

            byte[] readArr = new byte[1024];//每次最多读取1KB
            int len = 0;//保存每次读取的字节长度

            //循环读取
            while ((len = inputStream.read(readArr)) != -1) {
                outputStream.write(readArr, 0, len);
            }

            System.out.println("[INFO]图片下载完成:" + source + "\n\t ->" + targetFile.getPath() + "(" + Math.floor(targetFile.length() / 1024) + "kb)");


        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {

            try {

                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }


    }


}

Main.java

package src.com.yuan;

public class Main {
    public static void main(String[] args) {
        Downloader downloader = new Downloader();
        downloader.start("./conf");
    }
}

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

config.properties

thread-num=10
target-dir=G:/foo

运行结果

在这里插入图片描述
在这里插入图片描述

我的github地址

多线程下载器

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要用Java实现一个多线程下载,需要以下步骤: 1. 获取要下载文件的大小。 2. 将文件分成多个块,每个线程负责下载一块。 3. 开启多个线程,每个线程负责下载它所分配的一块。 4. 每个线程在下载完成后,将它下载的数据写入文件中的对应位置。 5. 当所有线程都完成下载后,合并文件块,得到完整的文件。 代码实现: ``` import java.io.*; import java.net.URL; import java.util.concurrent.*; public class MultiThreadDownloader { private static final int THREAD_COUNT = 4; public static void download(String url, String fileName) throws Exception { URL downloadURL = new URL(url); long contentLength = downloadURL.openConnection().getContentLengthLong(); ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT); CountDownLatch latch = new CountDownLatch(THREAD_COUNT); long blockSize = contentLength / THREAD_COUNT; for (int i = 0; i < THREAD_COUNT; i++) { long startIndex = i * blockSize; long endIndex = (i == THREAD_COUNT - 1) ? contentLength : startIndex + blockSize - 1; executor.submit(new DownloadTask(downloadURL, startIndex, endIndex, fileName, latch)); } latch.await(); executor.shutdown(); } private static class DownloadTask implements Runnable { private URL downloadURL; private long startIndex; private long endIndex; private String fileName; private CountDownLatch latch; public DownloadTask(URL downloadURL, long startIndex, long endIndex, String fileName, CountDownLatch latch) { this.downloadURL = downloadURL; this.startIndex = startIndex; this.endIndex = endIndex; this.fileName = fileName; this.latch = latch; } @Override public void run() { try { RandomAccessFile file = new RandomAccessFile(fileName, "rw"); file.seek(startIndex); InputStream inputStream = downloadURL.openConnection().

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值