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);
  }

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用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、付费专栏及课程。

余额充值