前言:用户需要下载的文件比较多,需要批量进行下载,并且将勾选下载的文件,打包成为一个压缩包后下载,这样用户所有的文件只需要下载一次,文件就全部在压缩包里面了。

实现:

一、hutool工具

hutool是一个比较全面的工具类,他内部提供了很多的工具方法,其中就有一个将文件流输出为压缩包的工具类和方法,这种实现方式是快捷简单的。

1. 引入依赖

<!--hutool工具类-->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.8.3</version>
</dependency>

2. 实现

在后端中,首先需要获取到所有文件流,这里采用的是从minio中获取相关文件,然后将文件全部打包输出的。参考演示代码如下:

@Override
public void exportToZip(HttpServletResponse response) {
// 一组需要导出的文件
List<String> fileNameList = Arrays.asList("1.xlsx", "2.xlsx", "3.xlsx");
String bucketName = "data";
try {
    // 遍历所有的文件,依次将文件加载到内存中
    String[] fileNames = new String[fileNameList.size()];
    InputStream[] ins = new InputStream[fileNameList.size()];
    for (int i = 0; i < fileNameList.size(); i++) {
        String fileName = fileNameList.get(i);
        InputStream in = minioClient.getObject(GetObjectArgs.builder()
                                               .bucket(bucketName)
                                               .object(fileName)
                                               .build());
        ins[i] = in;
        fileNames[i] = fileName;
    }
    response.setCharacterEncoding("UTF-8");
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("template1.zip", "UTF-8"));
    response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
    // 多个文件压缩成压缩包返回
    ZipUtil.zip(response.getOutputStream(), fileNames, ins);
} catch (Exception e) {
    log.error(e.getMessage());
    throw new GlobalException("文件下载异常!!!");
}
}

在前端中,就不需要处理关于zip压缩包的信息,只需要接受响应流,然后下载文件即可,前端参考示例代码如下:

const downloadFile = () => {
  console.log('文件开始下载了');
  let attachUrl = 'xls/1.zip';
  downloadBlob(attachUrl);
}

//下载流
const downloadBlob = (url) => {
  let newUrl = "";
  axios.request({
    method: "GET",
    url: 'http://localhost:8096/export/zip',
    responseType: "blob",
  }).then((res) => {
    newUrl = URL.createObjectURL(res.data);
    let filename = res.headers; 
    filename = filename["content-disposition"];
    filename = filename.split(";")[1].split("filename=")[1];
    let downloadElement = document.createElement("a");
    downloadElement.href = newUrl;
    downloadElement.download = decodeURI(filename);
    document.body.appendChild(downloadElement);
    downloadElement.click();
    document.body.removeChild(downloadElement);
  });
}

只需要上面简单的几步,就能将minio中的文件批量下载,并打包输出。

二、使用zip流

关于导出zip压缩包的方式,除了使用hutool的工具之外,还可以使用zip的输出流的方式,也就是ZipOutputStream实现。

实现方式参考如下:

这里使用的是Poi-TL生成word之后,将所有的word文档打包后输出,关于poi-TL,可以参考主业文章,这里不多做介绍。

/**
     * poi-ti渲染多个word之后,导出为zip压缩包格式
     */
public void exportFromPoiTl(HttpServletResponse response) {
String bucketName = "file";
String fileName = "demo.docx";
InputStream in = null;
// 渲染数据
try {
    response.setCharacterEncoding("UTF-8");
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("1.zip", "UTF-8"));
    response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
    ServletOutputStream os = response.getOutputStream();
    // 创建压缩包输出流
    ZipOutputStream zos = new ZipOutputStream(os);
    for (int i = 0; i < 3; i++) {
        // 获取文件模板
        in = minioClient.getObject(GetObjectArgs.builder()
                                   .bucket(bucketName)
                                   .object(fileName)
                                   .build());
        // 创建需要压缩的文件
        ZipEntry zipEntry = new ZipEntry(i + ".docx");
        zos.putNextEntry(zipEntry);
        // 取消文件大小限制
        ZipSecureFile.setMinInflateRatio(-1.0d);
        // poitl渲染数据
        XWPFTemplate template = XWPFTemplate.compile(in).render(
            new HashMap<String, Object>() {{
                put("title", "文件1");
            }});
        template.write(zos);
    }
    // 关闭流
    zos.close();
    os.close();
} catch (Exception e) {
    log.error("poiTl批量导出失败:" + e.getMessage());
} finally {
    // 关闭流
    if (null != in) {
        try {
            in.close();
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }
}
}

可以看到在第16行,创建了zip输出流:ZipOutputStream zos = new ZipOutputStream(os);,然后在循环中,创建了需要压缩的文件,ZipEntry zipEntry = new ZipEntry(i + ".docx");并将其设置到了zip包中,zos.putNextEntry(zipEntry);,并在此处设置了导出文件大小的限制,ZipSecureFile.setMinInflateRatio(-1.0d);,最终将数据以zip流的格式进行写出即可,别忘了关闭这些流。

总结:

关于将文件压缩之后输出为压缩包的方式本人暂时了解的有以上两种,基本可以满足日常的业务使用需求,如有不足,欢迎批评指正。