Java 实现多线程文件下载管理器

文件下载是许多程序中常见的功能,尤其是在处理大量文件的下载时,常常会遇到性能瓶颈。一个常见的挑战是如何有效地管理多个文件下载任务,同时确保每个下载任务的执行不会互相干扰,并且尽量优化下载效率。

本示例实现了一个多线程文件下载管理器,支持以下功能:

  1. 多线程下载: 同时支持多个文件的并发下载。
  2. 断点续传: 支持下载中断后的恢复。
  3. 下载进度监控: 实时输出下载进度。
  4. 任务管理: 管理多个文件的下载任务,支持暂停、取消等操作。

代码实现:

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

// 文件下载管理器
public class MultiThreadedDownloadManager {
    private final ExecutorService downloadExecutor;
    private final Map<String, DownloadTask> downloadTasks;
    private final AtomicInteger activeDownloads;

    public MultiThreadedDownloadManager(int maxThreads) {
        this.downloadExecutor = Executors.newFixedThreadPool(maxThreads);
        this.downloadTasks = new HashMap<>();
        this.activeDownloads = new AtomicInteger(0);
    }

    // 启动下载任务
    public void downloadFile(String fileUrl, String destinationPath) {
        DownloadTask task = new DownloadTask(fileUrl, destinationPath);
        downloadTasks.put(fileUrl, task);
        downloadExecutor.submit(task);
    }

    // 下载任务
    private class DownloadTask implements Runnable {
        private final String fileUrl;
        private final String destinationPath;
        private volatile boolean paused = false;
        private volatile boolean cancelled = false;

        public DownloadTask(String fileUrl, String destinationPath) {
            this.fileUrl = fileUrl;
            this.destinationPath = destinationPath;
        }

        @Override
        public void run() {
            try {
                downloadFileWithResume(fileUrl, destinationPath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 下载文件,支持断点续传
        private void downloadFileWithResume(String fileUrl, String destinationPath) throws IOException {
            URL url = new URL(fileUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            // 如果文件已经下载过,断点续传
            File destinationFile = new File(destinationPath);
            long existingFileSize = 0;
            if (destinationFile.exists()) {
                existingFileSize = destinationFile.length();
                connection.setRequestProperty("Range", "bytes=" + existingFileSize + "-");
            }

            // 获取文件大小
            long totalSize = connection.getContentLengthLong();
            try (InputStream inputStream = connection.getInputStream();
                 RandomAccessFile outputFile = new RandomAccessFile(destinationFile, "rw")) {

                // 如果文件存在且大小相等,说明下载完成
                if (existingFileSize >= totalSize) {
                    System.out.println("File already fully downloaded: " + destinationPath);
                    return;
                }

                outputFile.seek(existingFileSize);
                byte[] buffer = new byte[1024];
                int bytesRead;
                long downloadedBytes = existingFileSize;
                long lastUpdateTime = System.currentTimeMillis();

                while ((bytesRead = inputStream.read(buffer)) != -1 && !cancelled) {
                    if (paused) {
                        synchronized (this) {
                            wait();  // 等待恢复
                        }
                    }

                    outputFile.write(buffer, 0, bytesRead);
                    downloadedBytes += bytesRead;

                    // 显示下载进度
                    long currentTime = System.currentTimeMillis();
                    if (currentTime - lastUpdateTime >= 1000) {
                        long progress = (downloadedBytes * 100) / totalSize;
                        System.out.println("Downloading: " + fileUrl + " - " + progress + "%");
                        lastUpdateTime = currentTime;
                    }
                }

                if (cancelled) {
                    System.out.println("Download cancelled: " + fileUrl);
                } else {
                    System.out.println("Download completed: " + fileUrl);
                }
            } finally {
                activeDownloads.decrementAndGet();
            }
        }

        // 暂停下载
        public void pauseDownload() {
            paused = true;
        }

        // 恢复下载
        public synchronized void resumeDownload() {
            paused = false;
            notify();
        }

        // 取消下载
        public void cancelDownload() {
            cancelled = true;
        }
    }

    // 停止所有下载任务
    public void shutdown() {
        downloadExecutor.shutdown();
    }

    // 获取当前正在下载的任务数量
    public int getActiveDownloadCount() {
        return activeDownloads.get();
    }

    // 测试方法
    public static void main(String[] args) throws InterruptedException {
        MultiThreadedDownloadManager downloadManager = new MultiThreadedDownloadManager(3);

        // 启动下载任务
        downloadManager.downloadFile("https://example.com/file1.zip", "file1.zip");
        downloadManager.downloadFile("https://example.com/file2.zip", "file2.zip");
        downloadManager.downloadFile("https://example.com/file3.zip", "file3.zip");

        // 模拟暂停和恢复
        Thread.sleep(5000);
        downloadManager.downloadTasks.get("https://example.com/file1.zip").pauseDownload();
        System.out.println("Download paused for file1.zip");

        Thread.sleep(5000);
        downloadManager.downloadTasks.get("https://example.com/file1.zip").resumeDownload();
        System.out.println("Download resumed for file1.zip");

        // 等待所有下载任务完成
        while (downloadManager.getActiveDownloadCount() > 0) {
            Thread.sleep(1000);
        }

        // 关闭下载管理器
        downloadManager.shutdown();
    }
}

代码解析:

  1. 下载管理器:

    • MultiThreadedDownloadManager 类管理多个下载任务。它使用线程池(ExecutorService)来同时处理多个文件的下载任务,避免阻塞主线程。
    • downloadExecutor 是线程池,能够并发执行多个下载任务,最大线程数由用户传入。
    • downloadTasks 是一个存储当前下载任务的映射,方便对特定任务进行操作(如暂停、恢复、取消)。
  2. 下载任务:

    • 每个下载任务通过 DownloadTask 类来管理。任务可以暂停、恢复或取消。下载过程中,任务会定期更新进度,并在每秒钟显示当前下载的百分比。
    • downloadFileWithResume 方法实现了断点续传的功能。它检查文件是否已经存在,如果存在则从上次下载位置继续下载。
    • 使用 RandomAccessFile 来实现断点续传功能,通过 seek() 方法跳过已下载的部分。
  3. 任务控制:

    • 暂停: 使用 pauseDownload 方法将 paused 标志设置为 true,并使用 wait() 来阻塞任务。
    • 恢复: 使用 resumeDownload 方法将 paused 设置为 false,并通过 notify() 恢复下载任务。
    • 取消: 使用 cancelDownload 方法将 cancelled 设置为 true,终止下载。
  4. 进度更新:

    • 每当下载一定的字节后,更新下载进度。进度以百分比的形式显示在控制台上。
  5. 任务管理:

    • 使用 activeDownloads 来跟踪当前正在下载的任务数量,方便在主线程中监控任务执行状态。

使用说明:

  1. 初始化下载管理器:

    • 创建一个 MultiThreadedDownloadManager 实例,并设置最大线程数(例如 3 个线程并发下载)。
    MultiThreadedDownloadManager downloadManager = new MultiThreadedDownloadManager(3);
    
  2. 启动文件下载:

    • 使用 downloadFile 方法启动一个下载任务,第一个参数为文件 URL,第二个参数为保存文件的本地路径。
    downloadManager.downloadFile("https://example.com/file1.zip", "file1.zip");
    
  3. 暂停和恢复下载:

    • 使用 pauseDownload 暂停下载任务,使用 resumeDownload 恢复下载任务。
    downloadManager.downloadTasks.get("https://example.com/file1.zip").pauseDownload();
    downloadManager.downloadTasks.get("https://example.com/file1.zip").resumeDownload();
    
  4. 取消下载:

    • 使用 cancelDownload 取消一个下载任务。
    downloadManager.downloadTasks.get("https://example.com/file1.zip").cancelDownload();
    
  5. 关闭下载管理器:

    • 使用 shutdown 方法关闭下载管理器,停止所有下载任务。
    downloadManager.shutdown();
    

示例输出:

Downloading: https://example.com/file1.zip - 10%
Downloading: https://example.com/file2.zip - 20%
Downloading: https://example.com/file1.zip - 20%
Download paused for file1.zip
Downloading: https://example.com/file1.zip - 30%
Download resumed for file1.zip
Downloading: https://example.com/file1.zip - 50%
Downloading: https://example.com/file3.zip - 10%
Download completed: https://example.com/file1.zip
Download completed: https://example.com/file2.zip
Download completed: https://example.com/file3.zip

总结:

这个多线程文件下载管理器实现了一个功能丰富且高效的下载系统。它不仅支持多线程并发下载,还可以进行断点续传、进度显示和任务管理等。对于需要同时下载多个文件或对下载任务进行控制的场景,它提供了极大的便利和性能优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值