Spring Boot里如何实现多个文件打包成.zip文件一起下载

Spring Boot里如何实现多个文件打包成.zip文件一起下载

思路

用文件流分别读取服务器硬盘上的文件,形成多个文件流,然后想办法压缩成个一个文件流写入到响应流即可。

方法

使用ZipOutputStream实现。
java.util.zip.ZipOutputStream是Java原生工具类,比较容易上手。

代码示例

先封装一个压缩工具类,代码如下所示。

package com.liuboss.common.utils.zipfile;

import com.liuboss.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Component
public class ZipFileUtil {
    /**
     * 打包成zip下载
     */
    public void downloadZipFile(HttpServletResponse response, ZipFileDTO fileDTO) {
        List<String> names = fileDTO.getFileNms();
        List<ByteArrayOutputStream> streams = fileDTO.getStreams();
        //输出Excel文件
        try (
                OutputStream output = response.getOutputStream();
                ZipOutputStream zipStream = new ZipOutputStream(output)
        ) {
            String filename = URLEncoder.encode(fileDTO.getZipFileNm(), StandardCharsets.UTF_8.toString());
            response.setHeader("Content-Disposition", "attachment;filename=" + filename + ";filename*=utf-8''" + filename);
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());

            //创建压缩文件,并进行打包
            for (int i = 0; i < names.size(); i++) {
                ZipEntry z = new ZipEntry(names.get(i));
                zipStream.putNextEntry(z);
                streams.get(i).writeTo(zipStream);
            }
            zipStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取一个文件,暂存入ByteArrayOutputStream
     *
     * @param fullPath
     * @return
     */
    public ByteArrayOutputStream getByteArrayOutputStream(String fullPath) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try (
                FileInputStream inputStream = new FileInputStream(fullPath)
        ) {
            //小数组大法读取文件流,提高性能
            byte[] buff = new byte[8 * 1024];
            int len;
            while (-1 != (len = inputStream.read(buff))) {
                outputStream.write(buff, 0, len);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BusinessException("文件读取错误");
        }
        return outputStream;
    }
}

这是定义的一个DTO对象,用于传递参数。

package com.liuboss.common.utils.zipfile;

import lombok.Getter;
import lombok.Setter;

import java.io.ByteArrayOutputStream;
import java.util.List;

@Setter
@Getter
public class ZipFileDTO {
    /**
     * 每个文件的文件名称
     */
    private List<String> fileNms;
    /**
     * 每个文件的流
     */
    private List<ByteArrayOutputStream> streams;
    /**
     * 定义的压缩文件的名称
     */
    private String zipFileNm;
}

这里是我一般定义业务异常的风格,实现RuntimeException。然后把显示给用户看的信息和给程序员看的信息分离。

package com.liuboss.common.exception;

import lombok.Getter;
import lombok.Setter;

/**
 * 可知的业务异常 等价于 BizException的含义,但是这里继承RuntimeException用于避免方法上加throws
 *
 * @author LYW
 * @create 2022-01-19
 */
@Setter
@Getter
public class BusinessException extends RuntimeException {

    /**
     * 详细的错误信息,用于给程序开发人员进行错误分析用
     */
    private String detailMsg;
    /**
     * 用户可看的信息,避免一大串的报错信息反馈给用户,userMsg要通俗易懂,言简意赅,一些用户不必要知道的信息统统用“系统异常,请联系管理员”代替
     */
    private String userMsg;

    public BusinessException(String msg) {
        super(msg);
        userMsg = msg;
    }

    public BusinessException(String msg, String detailMsg) {
        super("userMsg:" + msg + ",detailMsg:" + detailMsg);
        this.userMsg = msg;
        this.detailMsg = detailMsg;
    }
}

具体你的业务逻辑层ServiceImpl,可以这么调用,如下所示。

 @Override
 public void downloadAll(HttpServletResponse response) {
	  //TODO 你的逻辑,假设你这个时候已经整理好了一个File数组。
      for (File one : files) {
          streams.add(zipFileUtil.getByteArrayOutputStream(one.getPath()));
          fileNms.add(one.getName());
      }

      ZipFileDTO zipFileDTO = new ZipFileDTO();
      zipFileDTO.setZipFileNm("样例压缩文件.zip");
      zipFileDTO.setStreams(streams);
      zipFileDTO.setFileNms(fileNms);
      zipFileUtil.downloadZipFile(response, zipFileDTO);
  }

好了,这样就大功告成了,希望能够对你有用,不妨动动你勤劳的小手给我点个赞或者留个言,谢谢!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
让你真正做到一行代码实现支付聚合,让你可以不用理解支付怎么对接,只需要专注你的业务 全能第三方支付对接pay-spring-boot-starter开发工具 开发工具在软件开发生命周期中扮演着至关重要的角色,它们旨在简化和加速从概念设计到产品部署的各个环节。以下是开发工具的主要作用: 代码编写与编辑: 提供集开发环境(IDE),如Visual Studio、Eclipse、Android Studio和Sublime Text等,这些工具集了文本编辑器,支持语法高亮、自动补全、代码片段管理和版本控制等功能,有助于开发者高效编写和维护代码。 项目管理: 支持项目创建、组织、构建自动化以及依赖管理,确保不同模块和组件之间的协调一致。 编译与构建: 括编译器、构建工具(如Make、Gradle、Maven)等,用于将源代码转换为可执行文件或库,并进行资源打包、优化等处理。 调试与测试: 集调试器允许开发者逐行执行代码,设置断点、查看变量值、跟踪调用堆栈等,帮助定位并修复代码中的错误。 测试框架和工具则协助开发者编写和运行单元测试、集测试及性能测试,确保软件质量。 版本控制与协作: 通过集Git、SVN等版本控制系统,支持团队员间的代码共享、分支管理、合并请求和冲突解决。 可视化设计与原型制作: 对于UI/UX设计,有界面设计工具,如Sketch、Adobe XD,可以帮助设计师快速构建应用程序界面模型,并生规范的设计稿供开发人员参考实现。 跨平台支持: 跨平台开发工具如Xamarin、React Native和Flutter,让开发者使用一种语言或框架编写可以在多个操作系统上运行的应用程序。 文档编写与API管理: 文档生工具可以自动生代码注释文档,便于团队内外理解和使用项目代码。 API管理工具则方便开发者创建、测试、发布和维护API接口。 持续集与持续部署(CI/CD): Jenkins、Travis CI、GitHub Actions等工具负责自动化构建、测试和部署流程,提高交付效率和可靠性。 数据库管理与ORM工具: 数据库客户端工具用于连接、查询、更新数据库,ORM(对象关系映射)工具简化了数据操作和持久化层的开发工作。 总之,开发工具极大地提升了软件工程师的工作效率,保证了开发过程中的准确性与一致性,同时也促进了团队合作,使得软件开发更系统化、规范化和工业化。
含了web开发中常用的功能,如:缓存(redis)、日志、事务、JPA、shiro、安全、常用工具类、swagger2在线接口文档、跨域支持等,可以基于该项目快速进行公司内部的项目开发。 开发工具在软件开发生命周期中扮演着至关重要的角色,它们旨在简化和加速从概念设计到产品部署的各个环节。以下是开发工具的主要作用: 代码编写与编辑: 提供集开发环境(IDE),如Visual Studio、Eclipse、Android Studio和Sublime Text等,这些工具集了文本编辑器,支持语法高亮、自动补全、代码片段管理和版本控制等功能,有助于开发者高效编写和维护代码。 项目管理: 支持项目创建、组织、构建自动化以及依赖管理,确保不同模块和组件之间的协调一致。 编译与构建: 括编译器、构建工具(如Make、Gradle、Maven)等,用于将源代码转换为可执行文件或库,并进行资源打包、优化等处理。 调试与测试: 集调试器允许开发者逐行执行代码,设置断点、查看变量值、跟踪调用堆栈等,帮助定位并修复代码中的错误。 测试框架和工具则协助开发者编写和运行单元测试、集测试及性能测试,确保软件质量。 版本控制与协作: 通过集Git、SVN等版本控制系统,支持团队员间的代码共享、分支管理、合并请求和冲突解决。 可视化设计与原型制作: 对于UI/UX设计,有界面设计工具,如Sketch、Adobe XD,可以帮助设计师快速构建应用程序界面模型,并生规范的设计稿供开发人员参考实现。 跨平台支持: 跨平台开发工具如Xamarin、React Native和Flutter,让开发者使用一种语言或框架编写可以在多个操作系统上运行的应用程序。 文档编写与API管理: 文档生工具可以自动生代码注释文档,便于团队内外理解和使用项目代码。 API管理工具则方便开发者创建、测试、发布和维护API接口。 持续集与持续部署(CI/CD): Jenkins、Travis CI、GitHub Actions等工具负责自动化构建、测试和部署流程,提高交付效率和可靠性。 数据库管理与ORM工具: 数据库客户端工具用于连接、查询、更新数据库,ORM(对象关系映射)工具简化了数据操作和持久化层的开发工作。 总之,开发工具极大地提升了软件工程师的工作效率,保证了开发过程中的准确性与一致性,同时也促进了团队合作,使得软件开发更系统化、规范化和工业化。
你可以使用JavaZipOutputStream类来实现文件打包ZIP并导出的功能。具体实现步骤如下: 1. 首先,你需要使用Java的File类找到需要打包文件夹,将其所有文件和子文件夹都遍历出来。 2. 然后,你需要使用JavaZipOutputStream类创建一个ZIP文件输出流,并将其与要生ZIP文件关联起来。 3. 接下来,你需要逐个将需要打包文件文件夹添加到ZIP文件中。对于文件夹,你需要递归地将其下的所有文件和子文件夹都添加到ZIP文件中。对于文件,你需要使用ZipEntry类创建一个ZIP文件条目,并将其添加到ZIP文件输出流中。 4. 最后,你需要关闭ZIP文件输出流。 下面是一个示例代码,假设你需要打包名为“myFolder”的文件夹: ```java import java.io.*; import java.util.zip.*; public class ZipFolder { public static void main(String[] args) throws Exception { String sourceFolder = "myFolder"; // 需要打包文件夹 String zipFile = "myArchive.zip"; // 生ZIP文件名 FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zos = new ZipOutputStream(fos); zipFolder(sourceFolder, sourceFolder, zos); zos.close(); fos.close(); System.out.println("ZIP文件已生:" + zipFile); } private static void zipFolder(String baseFolder, String sourceFolder, ZipOutputStream zos) throws Exception { File folder = new File(sourceFolder); for (File file : folder.listFiles()) { if (file.isDirectory()) { zipFolder(baseFolder, file.getAbsolutePath(), zos); } else { String entryName = file.getAbsolutePath().substring(baseFolder.length() + 1); ZipEntry zipEntry = new ZipEntry(entryName); zos.putNextEntry(zipEntry); FileInputStream fis = new FileInputStream(file); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) > 0) { zos.write(buffer, 0, len); } zos.closeEntry(); fis.close(); } } } } ``` 这个示例代码将会打包名为“myFolder”的文件夹,并生名为“myArchive.zip”的ZIP文件。你可以根据自己的需要修改代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值