Skip to content

zip 压缩包导出实现 #42

@zhangwt-cn

Description

@zhangwt-cn

导出操作流程

  1. 一对多数据导出到 Excel 内
  2. 数据关联的图片需要导出到文件夹
  3. 将 Excel 文件、文件夹打包成 Zip 压缩包并写入接口响应流

相关依赖

  1. Minio
  2. EasyPOI1

实现代码

导出 zip 压缩包结构

export.zip
├── export.xlsx                   # Excel 文件直接放在 ZIP 的根目录
└── file/
    ├── name1/
    │   ├── image1.jpg               # 商品1的图片文件
    │   └── image2.jpg
    └── name2/
        ├── image3.jpg               # 商品2的图片文件
        └── image4.jpg

关键代码(文件流写入 Zip)

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())
// create ZIP entry
ZipEntry zipEntry = new ZipEntry("export.xlsx");
zos.putNextEntry(zipEntry);

// Excel write to ZIP
zos.write(baos.toByteArray());
zos.closeEntry();

具体实现

1. 导出 Excel 实体类

public class ExportData {
    @Excel(name = "名称", needMerge = true)
    private String name;
    @ExcelCollection(name = "子数据")
    private List<ExportDataChildren> children;
}

2. 导出子数据类

public class ExportDataChildren {
    @Excel(name = "名称")
    private String name;
}

3. 文件类

public class ExportFile {
    // minio url
    private String url;
}

4. 业务代码

class ExportDataService {

    public void exportDataZip(HttpServletResponse response) {
        // get export data
        List<ExportData> exportList = getExportData();
        try (Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), ExportData.class, exportList);
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            // Workbook write to ByteArrayOutputStream
            workbook.write(baos);

            // create ZIP entry
            ZipEntry zipEntry = new ZipEntry("export.xlsx");
            zos.putNextEntry(zipEntry);

            // Excel write to ZIP
            zos.write(baos.toByteArray());
            zos.closeEntry();

            // export file
            exportList.forEach(exportData -> {
                List<ExportFile> exportFileList = getExportFile();
                exportFileList.forEach(exportFile -> {
                    try (InputStream inputStream = download(exportData.getUrl())) {
                        byte[] bytes = IoUtil.readBytes(inputStream);
                        String folderPath = "file/" + exportData.getName() + "/";
                        String name = FileUtil.getName(exportFile.getUrl());
                        ZipEntry entry = new ZipEntry(folderPath + name);
                        zos.putNextEntry(entry);
                        zos.write(bytes);
                        zos.closeEntry();
                    } catch (Exception e) {
                        log.error("export file error: {}", e.getMessage());
                    }
                });
            });

            zos.finish();
            response.flushBuffer();
        } catch (Exception e) {
            log.error("export zip error: {}", e.getMessage());
        }
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=\"export.zip\"");
    }
    
    // get file InputStream
    public InputStream download(String url) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(YOUR_BUCKET)
                            .object(url)
                            .build()
            );
        } catch (Exception e) {
            log.error("download file error", e);
            throw new BusinessException("文件下载异常");
        }
    }
    
     public List<ExportData> getExportData() {
         // 获取数据库数据
     }
     
     public List<ExportFile> getExportFile() {
         // 获取数据库文件地址
     }
    
}

5. 代码实现流程图

flowchart TD
    A[开始] --> B1[接收导出请求参数]
    B1 --> B2[参数校验]
    B2 --> |校验通过| B3[查询物料基础数据]
    B2 --> |校验失败| E1[返回错误信息]
    E1 --> H[结束]
    
    B3 --> C[EasyPoi处理] & D[MinIO图片处理]
    
    subgraph EasyPoi处理
        C --> C1[创建ExportParams配置]
        C1 --> C2[设置Excel样式和参数]
        C2 --> C3[构建@Excel注解实体类]
        C3 --> C4[准备导出数据List]
        C4 --> C5[使用ExcelExportUtil.exportExcel]
        C5 --> C6[获取Workbook字节数组流]
    end
    
    subgraph MinIO图片处理
        D --> D1[创建MinIO客户端]
        D1 --> D2[遍历物料图片数据]
        D2 --> D3{是否有图片?}
        D3 --> |是| D4[通过MinIO获取图片对象流]
        D3 --> |否| D7[跳过处理]
        D4 --> D5[创建以物料名称为路径的ZIP条目]
        D5 --> D6[写入图片字节流到ZIP输出流]
        D6 --> D8[检查数据完整性]
        D7 --> D9[继续处理下一个]
        D8 --> D9
    end
    
    C6 --> E[ZIP流处理]
    D9 --> E
    
    E --> F1[创建ByteArrayOutputStream]
    F1 --> F2[创建ZipOutputStream]
    F2 --> F3[写入Excel并创建entry]
    F3 --> F4[写入图片并创建对应entry]
    F4 --> F5[完成ZIP字节数组]
    
    F5 --> G1[设置响应头ContentType等]
    G1 --> G2[写入响应输出流]
    G2 --> G3[关闭所有流资源]
    G3 --> H[结束]
    
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style H fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#bbf,stroke:#333,stroke-width:2px
    style F1 fill:#bbf,stroke:#333,stroke-width:2px
    style F2 fill:#bbf,stroke:#333,stroke-width:2px
    style F3 fill:#bbf,stroke:#333,stroke-width:2px
    style F4 fill:#bbf,stroke:#333,stroke-width:2px
    style B2 fill:#ffd,stroke:#333,stroke-width:2px
    style D3 fill:#ffd,stroke:#333,stroke-width:2px
    
    classDef note fill:#fff,stroke:#333,stroke-width:1px
    class C1,C2,C3,C4,C5,D1,D4 note

Loading

Footnotes

  1. EasyPOI 参考博客

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions