Java多线程下载单个文件

package com.example.test.demo;

import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class 多线程下载单个文件 {
    private static final int BUFFER_SIZE = 4096;
    //线程数量
    private static final int THREAD_COUNT = 20;

    public static void main(String[] args) {
        //源文件路径
        //String fileUrl = "https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.11/bin/apache-tomcat-9.0.11.exe";
        String fileUrl = "https://download5.mcloud.139.com/storageWeb/servlet/downloadServlet?code=TDAxNDExbDRoQVQzbnYzMjkxN3UybkFBZFdS&un=1A2CCFE331C8B0519052E6BA4AEDAF215DB052323B43DE4378B4E066AF941ABB&dom=D950&rate=0&txType=0";
        //设置目标文件存放位置
        String savePath = "E:\\下载\\Java\\" ;

        //开始下载
        multiThreadedDownloadOfFiles(fileUrl,savePath);
    }


    /**
     * 多线程下载文件
     * @param fileUrl 源文件url
     * @param savePath  目标文件路径
     */
    public static void multiThreadedDownloadOfFiles(String fileUrl,String savePath){
        long startTime = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
        try {
            URL url = new URL(fileUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            String filename = connection.getHeaderField("Content-Disposition");
            if (StringUtils.isEmpty(filename)){
                String file = url.getFile();
                //获取源文件名
                filename = file.substring(file.lastIndexOf('/') + 1);
            }else {
                String[] split = filename.split("\"");
                filename=split[1];
            }

            //设置目标文件路径
            final String saveDir= savePath + filename;

            //源文件大小
            int fileSize = connection.getContentLength();

            //文件分段名称
            String partitionName = UUID.randomUUID().toString().replace("-", "");

            for (int i = 0; i < THREAD_COUNT; i++) {
                final int threadId = i;
                int startByte = (fileSize / THREAD_COUNT) * threadId;
                int endByte = (fileSize / THREAD_COUNT) * (threadId + 1) - 1;
                if (threadId == THREAD_COUNT - 1) {
                    endByte = fileSize - 1;
                }
                final int endByte2 = endByte;
                executorService.execute(() -> {
                    try {
                        //分段下载文件
                        downloadPartialFile(fileUrl, saveDir, startByte, endByte2, threadId, partitionName);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            }

            executorService.shutdown();
            //等待所有线程执行完毕
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
            //合并文件
            mergePartialFiles(saveDir, THREAD_COUNT, partitionName);
            long endTime = System.currentTimeMillis();
            System.err.println("下载耗时:" + (endTime - startTime));
        } catch (Exception e) {
            e.printStackTrace();
            executorService.shutdown();
        }
    }

    /**
     * 分段下载文件
     *
     * @param fileUrl       源文件路径
     * @param saveDir       设置目标文件路径
     * @param startByte     开始位置
     * @param endByte       结束位置
     * @param threadId      第几个分片
     * @param partitionName 文件分段名称
     * @throws IOException
     */
    private static void downloadPartialFile(String fileUrl, String saveDir, int startByte, int endByte, int threadId, String partitionName) throws IOException {
        // 创建URL对象并打开文件连接
        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 设置HTTP请求头,指定下载的字节范围
        connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte);

        // 创建输入流并打开缓冲区
        BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream());

        // 创建文件输出流并将流定向到指定的保存目录
        FileOutputStream fileOutputStream = new FileOutputStream(saveDir + partitionName + threadId + ".ext");

        // 创建缓冲区
        byte[] buffer = new byte[BUFFER_SIZE];
        int bytesRead;

        // 读取输入流并写入文件输出流,直到读取到文件末尾
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            fileOutputStream.write(buffer, 0, bytesRead);
        }

        // 关闭文件输出流和输入流
        fileOutputStream.close();
        inputStream.close();

        // 打印已下载的文件范围
        System.out.println("已下载部分文件: " + startByte + " - " + endByte);
    }

    /**
     * 合并文件
     *
     * @param saveDir       设置目标文件路径
     * @param partitionName 文件分段名称
     * @throws IOException
     */
    private static void mergePartialFiles(String saveDir, int threadCount, String partitionName) throws IOException {
        // 新建一个随机访问文件输出流,指定文件路径和打开模式为可读写
        RandomAccessFile outputFile = new RandomAccessFile(saveDir, "rw");

        // 遍历线程数
        for (int i = 0; i < threadCount; i++) {
            // 拼接出分区文件名,指定文件路径、分区名和线程号
            String partitionFileName = saveDir + partitionName + i + ".ext";

            // 新建一个随机访问文件输入流,指定文件路径和打开模式为只读
            RandomAccessFile partialFile = new RandomAccessFile(partitionFileName, "r");

            // 定义一个缓冲区数组,指定缓冲区的大小
            byte[] buffer = new byte[BUFFER_SIZE];

            // 定义一个变量用于保存每次读取的字节数
            int bytesRead;

            // 循环读取每个分区文件的内容,直到到达文件末尾
            while ((bytesRead = partialFile.read(buffer)) != -1) {
                // 将读取的内容写入输出文件流
                outputFile.write(buffer, 0, bytesRead);
            }

            // 关闭分区文件流
            partialFile.close();

            // 删除分区文件
            File aa = new File(partitionFileName);
            aa.delete();
        }

        // 关闭输出文件流
        outputFile.close();
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值