多线程并行下载文件到浏览器

前面记录到从FTP服务器下载到浏览器,使用缓冲流也没有提升速度。

现在想要通过多线程并行下载看能不能提升,将文件拆分,每个线程负责下载一部分文件到输出流。

每个线程不能共用一个输入流,目前试过是不好控制,会导致下载的数据漏掉一部分。我的做法是在每个线程里面都创建要下载的文件输入流,每个线程的输入流是一样的。

@Override
    // 下载文件,使用缓冲流 + 多线程
    public void downloadFiles(String filePath, HttpServletResponse response) {
        String[] split = filePath.split("/");
        String dirPath = split[1];
        String fileName = split[2];
        long fileSize = 0L;

        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            ftpClient.changeWorkingDirectory(dirPath);
            fileSize = Long.parseLong(ftpClient.getSize(fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 设置响应头,告诉浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

        try (
                ServletOutputStream out = response.getOutputStream();
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out)
        ) {

            int threadNums = 3;
            // 线程池任务计数器
            CountDownLatch countDownLatch = new CountDownLatch(threadNums);
            // 计算每个线程下载的字节数
            long chunkSize = fileSize / threadNums;

            long startByte = 0;
            long endByte = chunkSize - 1;
            for (int i = 0; i < threadNums; i++) {
                if (i == threadNums - 1) {
                    // 最后一个线程下载剩余的部分
                    endByte = fileSize;
                }

                executor.execute(new DownloadFileTask(startByte, endByte, bufferedOutputStream, countDownLatch, ftpPoolService, fileName));

                startByte = endByte + 1;
                endByte += chunkSize;

            }

            // 等待所有任务完成
            countDownLatch.await();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {

            ftpPoolService.returnObject(ftpClient);

        }
    }

 

package com.lin.thread.task;

import com.lin.ftp.service.FTPPoolService;
import org.apache.commons.net.ftp.FTPClient;

import java.io.*;
import java.util.concurrent.CountDownLatch;

public class DownloadFileTask implements Runnable{

    private long startByte;// 输入流开始读取位置
    private long endByte;// 输入流结束读取位置

    private long bytes;// 当前线程要写入的字节大小
    
    private OutputStream outputStream;// 要写入的输出流

    private CountDownLatch countDownLatch;// 线程池任务计数器

    private FTPPoolService ftpPoolService;// ftp服务

    private String fileName;// 要下载的文件名

    public DownloadFileTask() {
    }


    public DownloadFileTask(long startByte, long endByte, BufferedOutputStream outputStream, CountDownLatch countDownLatch,FTPPoolService ftpPoolService,String fileName) {
        this.startByte = startByte;
        this.endByte = endByte;
        this.outputStream = outputStream;
        this.countDownLatch = countDownLatch;
        this.ftpPoolService = ftpPoolService;
        this.fileName = fileName;
        this.bytes = endByte - startByte;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":" + startByte + "---" + endByte);

        byte[] buffer = new byte[2048];
        int bytesRead = 0;
        long writtenBytes = 0;
        int remainingBytes = 0;

        FTPClient ftpClient = ftpPoolService.borrowObject();
        try {
            ftpClient.changeWorkingDirectory("files");
        } catch (IOException e) {
            e.printStackTrace();
        }
        try(InputStream inputStream = ftpClient.retrieveFileStream(fileName);
            BufferedInputStream bis = new BufferedInputStream(inputStream)) {

            // 输入流跳到开始下载的位置
            bis.skip(startByte);

            while ((bytesRead = bis.read(buffer)) != -1) {
                // 达到下载范围的末尾,停止写入
                if (writtenBytes + bytesRead > bytes) {
                    remainingBytes = (int) (bytes - writtenBytes);
                    outputStream.write(buffer, 0, remainingBytes);
                    break;
                }

                outputStream.write(buffer, 0, bytesRead);
                writtenBytes += bytesRead;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                ftpClient.completePendingCommand();
            } catch (IOException e) {
                e.printStackTrace();
            }
            ftpPoolService.returnObject(ftpClient);
        }
        System.out.println(Thread.currentThread().getName() + "写入大小:" + writtenBytes + ". 总的要写:" + bytes  + ". 多出写入:" + remainingBytes);
        countDownLatch.countDown();
    }
}

你可能感兴趣的:(Java,java)