Java实现字节流压缩ZIP包并下载

业务场景:系统有一个数据列表,用户点击某行下载按钮,系统可以将该行数据转化成WORD文档的形式供用户下载查看,这里直接用 XWPFTemplate 可以轻松实现,但是数据太多的情况下用户不可能每条数据都点击下载,不仅耗时而且会在浏览器下载非常多的文件,用户不好整理,体验感较差,于是有了新需求:用户可以多选数据行,然后系统压缩成一个ZIP包,所有文件都在ZIP包里,用户只需要下载一个ZIP即可。

简而言之,就是通过ZIP压缩包的形式实现批量下载

代码实现(只贴了核心代码):

1. 封装方法

    public byte[] zipFile(List<DownloadFileDto> downloadFileDtoList) throws Exception {

        // 将字节写到一个字节输出流里
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream out = new ZipOutputStream(baos)) {
            // 创建zip file in memory
            for (DownloadFileDto downloadFileDto : downloadFileDtoList) {
                ZipEntry entry = new ZipEntry(downloadFileDto.getFileName());
                entry.setSize(downloadFileDto.getByteDataArr().length);
                out.putNextEntry(entry);
                out.write(downloadFileDto.getByteDataArr());
                out.closeEntry();
            }
        } catch (IOException e) {
            logger.error("压缩zip数据出现异常", e);
            throw new RuntimeException("压缩zip包出现异常");
        }
        return baos.toByteArray();
    }

2. 封装DTO

/**
 * @author YuePeng
 * @date 2022/12/22 11:52
 * 存放文件字节流和文件名
 */
public class DownloadFileDto implements Serializable {

    private static final long serialVersionUID = 2648469408255550980L;
    
    // 存放文件名
    private String fileName = "";
    
    // 存放文件字节流
    private byte[] byteDataArr = new byte[0];

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public byte[] getByteDataArr() {
        return byteDataArr;
    }

    public void setByteDataArr(byte[] byteDataArr) {
        this.byteDataArr = byteDataArr;
    }

    @Override
    public String toString() {
        return "DownloadFileDto{" +
                "fileName='" + fileName + '\'' +
                ", byteDataArr=" + Arrays.toString(byteDataArr) +
                '}';
    }
}

3. Controller层

    @ApiOperation(value = "批量下载", notes = "下载ZIP压缩包")
    @RequestMapping(value = "/downloadZip", method = RequestMethod.GET)
    public ResponseEntity downloadZip(
            @RequestParam List<String> ids, HttpServletResponse response) throws Exception {
        // ids即为前端传过来的的行数据id集合
        if (ids.isEmpty()) {
            return new ResponseEntity(HttpStatus.OK);
        }
        List<DownloadFileDto> downloadFileDtoList = new ArrayList<>();
        // 这里的i的作用是保证文件名唯一,否则往ZIP中添加文件会报异常
        int i = 1;  
        for (String id : ids) {
            // 你的每一个文件字节流
            byte[] bytes = phvEquipReportService.downloadProveTable(id);
            String fileName = i + ".docx";
            DownloadFileDto dto = new DownloadFileDto();
            dto.setFileName(fileName);
            dto.setByteDataArr(bytes);
            downloadFileDtoList.add(dto);
            i++;
        }
        // FIXME 文件名最好为英文,中文的话下载的文件名会乱码,暂时未解决
        String fileName = "download.zip";
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(StandardCharsets.UTF_8), "ISO8859-1"));
        byte[] dataByteArr = new byte[0];
        if (CollectionUtils.isNotEmpty(downloadFileDtoList)) {
            try {
                dataByteArr = zipFile(downloadFileDtoList);
                response.getOutputStream().write(dataByteArr);
                response.flushBuffer();
            } catch (Exception e) {
                logger.error("压缩zip数据出现异常", e);
                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
            }
        }
        return new ResponseEntity(dataByteArr, HttpStatus.OK);
    }

本文参考:java将字节数组写成zip文件,压缩字节数组成zip包并下载_秋冬将至的博客-CSDN博客


版权声明:本文为MakkaPakka_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。