前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java实现多文件压缩下载

Java实现多文件压缩下载

作者头像
Li_XiaoJin
发布2022-06-10 18:42:28
1.6K0
发布2022-06-10 18:42:28
举报
文章被收录于专栏:Lixj's Blog

最近遇到的一个需求,记录一下解决方案~

需求分析

权限审批项目中有个场景是页面上展示多个附件,点击全部下载是下载全部文件,并将其下载成压缩包。

页面上展示的应该是多个链接,到时让前端传多个链接,后台进行获取文件服务器的文件进行压缩处理,再进行下载。

这里我选的是边压缩 ZIP 格式文件边下载(直接输出ZIP流)。

ZipOutPutStream 的使用:

  1. 创建 ZipOutPutStream 流,利用 BufferedOutputStream 提个速.
  2. 利用 putNextEntry 来将目录点写入
  3. 递归目录数组
  4. 写入数据,关闭流

示例 demo

以下是实现的示例 demo。

下载 Controller 实现,DownloadFileController.java

代码语言:javascript
复制

import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author lixiaojin
 * @date 2021/11/21 13:17
 */
@Api(tags = "文件相关接口")
@RestController
@RequestMapping("/api/v1/file_down")
@Slf4j
public class DownloadFileController {


    /**
     * 压缩本地文件
     */
    @GetMapping("/download_1")
    public void downloadOne(HttpServletResponse response) {

        // 1、本地文件列表
        List<File> files = new ArrayList<>();
        files.add(new File("D:\\data\\test.png"));
        files.add(new File("D:\\data\\test.png"));
        files.add(new File("D:\\data\\Test.pdf"));
        // 检查需要下载多文件列表中文件路径是否都存在
        for (File file : files) {
            if (!file.exists()) {
                // 需要下载的文件中存在不存在地址
                return;
            }
        }

        // 2、响应头的设置
        String downloadName = UUIDUtil.getUuid() + ".zip";
        CompressDownloadUtil.setDownloadResponse(response, downloadName);

        try {
            // 3、将多个文件压缩写进响应的输出流
            CompressDownloadUtil.compressZip(files, response.getOutputStream());
        } catch (IOException e) {
            log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
        }

    }


    /**
     * 压缩远程服务器文件
     */
    @GetMapping("/download_2")
    public void downloadTwo(HttpServletResponse response) {
        // 1、文件路径,可以由前端传入
        List<String> fileList = new ArrayList<>();
        fileList.add("http://199.199.2.135/filer/2021-11/test_20211119160019989.png");
        fileList.add("http://199.199.2.135/filer/2021-11/test_20211119160019989.png");
        fileList.add("http://199.199.2.135/filer/2021-11/确认表_20211112173714929.pdf");

        //-- 2、响应头的设置
        String downloadName = UUIDUtil.getUuid() + ".zip";
        CompressDownloadUtil.setDownloadResponse(response, downloadName);


        try {
            //-- 3、将多个文件压缩写进响应的输出流
            CompressDownloadUtil.compressZipByName(fileList, response.getOutputStream());
        } catch (IOException e) {
            log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
        }

    }

}

压缩文件工具类,

代码语言:javascript
复制

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * @author lixiaojin
 * @date 2021/11/21 13:30
 */
@Slf4j
public class CompressDownloadUtil {

    private CompressDownloadUtil() {}

    /**
     * 设置下载响应头
     */
    public static HttpServletResponse setDownloadResponse(HttpServletResponse response, String downloadName) {
        response.reset();
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;fileName*=UTF-8''"+ downloadName);
        return response;
    }

    public static Integer[] toIntegerArray(String param) {
        return Arrays.stream(param.split(","))
                .map(Integer::valueOf)
                .toArray(Integer[]::new);
    }

    /**
     * 将多个文件压缩到指定输出流中
     *
     * @param files 需要压缩的文件列表
     * @param outputStream  压缩到指定的输出流
     */
    public static void compressZip(List<File> files, OutputStream outputStream) {
        // 包装成ZIP格式输出流
        try (ZipOutputStream zipOutStream = new ZipOutputStream(new BufferedOutputStream(outputStream))) {
            // 设置压缩方法
            zipOutStream.setMethod(ZipOutputStream.DEFLATED);
            // 将多文件循环写入压缩包
            for (int i = 0; i < files.size(); i++) {
                File file = files.get(i);
                FileInputStream filenputStream = new FileInputStream(file);
                byte[] data = new byte[(int) file.length()];
                filenputStream.read(data);
                // 添加ZipEntry,并ZipEntry中写入文件流,这里,加上i是防止要下载的文件有重名的导致下载失败
                zipOutStream.putNextEntry(new ZipEntry(i+ "-" + file.getName()));
                zipOutStream.write(data);
                filenputStream.close();
                zipOutStream.closeEntry();
            }
        } catch (IOException e) {
            log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
        }  finally {
            try {
                if (Objects.nonNull(outputStream)) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
            }
        }
    }

    private static HttpResponse invokeGetFile(String url, CloseableHttpClient httpclient) {

        HttpResponse response;
        try {
            HttpGet get = new HttpGet(url);
            get.setHeader("Content-type", "UTF-8");
            get.setHeader("Connection", "close");
            response = httpclient.execute(get);

        } catch (Exception e) {
            log.error("请求文件异常");
            return null;
        }
        return response;
    }

    public static void compressZipByName(List<String> files, OutputStream outputStream) {
        // 包装成ZIP格式输出流
        try (ZipOutputStream zipOutStream = new ZipOutputStream(new BufferedOutputStream(outputStream))) {
            //  设置压缩方法
            zipOutStream.setMethod(ZipOutputStream.DEFLATED);
            // 将多文件循环写入压缩包
            for (int i = 0; i < files.size(); i++) {
                CloseableHttpClient httpclient = HttpClients.createMinimal();
                String fileName = getFileName(files.get(i));
                HttpResponse response = invokeGetFile(files.get(i), httpclient);
                InputStream inputStream = response.getEntity().getContent();
                response.getEntity().getContentLength();
                byte[] data = new byte[(int) response.getEntity().getContentLength()];
                inputStream.read(data);
                //  添加ZipEntry,并ZipEntry中写入文件流,这里,加上i是防止要下载的文件有重名的导致下载失败
                zipOutStream.putNextEntry(new ZipEntry(i+ "-" + fileName));
                zipOutStream.write(data);
                inputStream.close();
                zipOutStream.closeEntry();
                httpclient.close();
            }
        } catch (IOException e) {
            log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
        }  finally {
            try {
                if (Objects.nonNull(outputStream)) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
            }
        }
    }

    /**
     * 获取文件名
     * @param fileUrl 文件地址
     * @return 文件名
     */
    private static String getFileName(String fileUrl) {
        String[] nameArr = fileUrl.split("/");
        return nameArr[nameArr.length - 1];
    }


    /**
     * 下载文件
     * @param outputStream 下载输出流
     * @param zipFilePath 需要下载文件的路径
     */
    public static void downloadFile(OutputStream outputStream, String zipFilePath) {
        File zipFile = new File(zipFilePath);
        if (!zipFile.exists()) {
            // 需要下载压塑包文件不存在
            return ;
        }
        try (FileInputStream inputStream = new FileInputStream(zipFile)) {
            byte[] data = new byte[(int) zipFile.length()];
            inputStream.read(data);
            outputStream.write(data);
            outputStream.flush();
        } catch (IOException e) {
            log.error(CompressDownloadUtil.class.getName(), "downloadZip", e);
        } finally {
            try {
                if (Objects.nonNull(outputStream)) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error(CompressDownloadUtil.class.getName(), "downloadZip", e);
            }
        }
    }

    /**
     * 删除指定路径的文件
     * @param filepath 文件路径
     */
    public static void deleteFile(String filepath) {
        File file = new File(filepath);
        deleteFile(file);
    }

    /**
     * 删除指定文件
     * @param file 文件
     */
    public static void deleteFile(File file) {
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists()) {
            file.delete();
        }
    }

}

测试

访问接口: http://localhost:9003/api/v1/file_down/download_1 http://localhost:9003/api/v1/file_down/download_2

测试完成~

问题记录

  1. 文件损坏、无法打开 如果前端下载出现文件损坏、无法打开等问题,检查一下前端代码中是否设置 responseType: ‘blob’,这一步是关键,写了也看检查一下是不是生效了,不然下载问题时就有问题。

End.

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可 Links: https://lixj.fun/archives/java实现多文件压缩下载

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求分析
  • 示例 demo
  • 测试
  • 问题记录
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档