skip to content
Wentao Zhang

zip 压缩包导出实现

/ 5 min read

导出操作流程

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

相关依赖

  1. Minio
  2. EasyPOI1

实现代码

导出 zip 压缩包结构

Terminal window
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 实体类

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

2. 导出子数据类

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

3. 文件类

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

4. 业务代码

ExportDataService.java
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. 代码实现流程图

MinIO图片处理
EasyPoi处理
校验通过
校验失败
创建MinIO客户端
MinIO图片处理
遍历物料图片数据
是否有图片?
通过MinIO获取图片对象流
跳过处理
创建以物料名称为路径的ZIP条目
写入图片字节流到ZIP输出流
检查数据完整性
继续处理下一个
创建ExportParams配置
EasyPoi处理
设置Excel样式和参数
构建@Excel注解实体类
准备导出数据List
使用ExcelExportUtil.exportExcel
获取Workbook字节数组流
开始
接收导出请求参数
参数校验
查询物料基础数据
返回错误信息
结束
ZIP流处理
创建ByteArrayOutputStream
创建ZipOutputStream
写入Excel并创建entry
写入图片并创建对应entry
完成ZIP字节数组
设置响应头ContentType等
写入响应输出流
关闭所有流资源

Footnotes

  1. EasyPOI 参考博客