【EasyExcel实践】导出多个sheet到多个excel文件,并压缩到一个zip文件

文章目录

  • 前言
  • 正文
    • 一、项目依赖
    • 二、封装表格实体和Sheet实体
      • 2.1 表格实体
      • 2.2 Sheet实体
    • 三、核心实现
      • 3.1 核心实现之导出为输出流
      • 3.2 web导出
      • 3.3 导出为字节数组
    • 四、调试
      • 4.1 构建调试用的实体类
      • 4.2 控制器调用
      • 4.3 测试结果
    • 五、注册大数转换器,长度大于15时,转换为字符串
      • 5.1 实现转换器
      • 5.2 使用转换器

前言

工作中遇到一个需求,一次导出多个Excel 文件,并且每个excel中可能存在1到多个sheet页。
好在没有那种单元格合并的要求。

总体的思路是,设计两个实体,一个表示表格,一个表示sheet 数据。并且表格包含一个list 类型的sheet对象。

然后再使用ZipOutputStreamExcelWriterBuilderEasyExcel#writerSheet(...) 等类和方法去组装表格,最终进行压缩。

项目整体使用 java 8 和 阿里的easyexcel工具包。

正文

一、项目依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.0.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.11</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></exclusion></exclusions></dependency></dependencies>

二、封装表格实体和Sheet实体

2.1 表格实体

/*** excel数据*/
@Data
public static class ExcelData {// sheet的列表private final List<ExcelShellData<?>> shellDataList = new ArrayList<>();// 表格文件名private String filename;public void addShellData(ExcelShellData<?> excelShellData) {this.shellDataList.add(excelShellData);}
}

2.2 Sheet实体

/*** sheet数据*/
@Data
@AllArgsConstructor
public static class ExcelShellData<T> {// 数据private List<T> list;// sheet名private String sheetName;// 数据实体类型private Class<T> clazz;
}

三、核心实现

3.1 核心实现之导出为输出流

这一步主要组装表格数据,以及生成sheet。最终将数据放到输出流outputStream中 。

private static void exportZipStream(List<ExcelData> excelDataList, OutputStream outputStream) {try {// 开始存入try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {try {for (ExcelData excelData : excelDataList) {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();com.alibaba.excel.ExcelWriter excelWriter = null;try {ExcelWriterBuilder builder = EasyExcel.write(byteArrayOutputStream).autoCloseStream(false)// 自动适配.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());excelWriter = builder.build();zipOut.putNextEntry(new ZipEntry(excelData.getFilename()));// 开始写入excelfor (ExcelShellData<?> shellData : excelData.getShellDataList()) {WriteSheet writeSheet = EasyExcel.writerSheet(shellData.getSheetName()).head(shellData.getClazz()).build();excelWriter.write(shellData.getList(), writeSheet);}} catch (Exception e) {throw new RuntimeException("导出Excel异常", e);} finally {if (excelWriter != null) {excelWriter.finish();}}byteArrayOutputStream.writeTo(zipOut);zipOut.closeEntry();}} catch (Exception e) {throw new RuntimeException("导出Excel异常", e);}}} catch (IOException e) {throw new RuntimeException("导出Excel异常", e);}}

3.2 web导出

可以调用本方法,直接在Controller中调用之后,当访问对应url,会直接下载到浏览器。

    /*** 导出多个sheet到多个excel文件,并压缩到一个zip文件*/public static void exportZip(String zipFilename, List<ExcelData> excelDataList, HttpServletResponse response) {try {// 这里URLEncoder.encode可以防止中文乱码zipFilename = URLEncoder.encode(zipFilename, "utf-8");// 指定文件名response.setHeader("Content-disposition", "attachment;filename=" + zipFilename);response.setContentType("application/x-msdownload");response.setCharacterEncoding("utf-8");exportZipStream(excelDataList, response.getOutputStream());} catch (IOException e) {throw new RuntimeException("导出Excel异常", e);}}

3.3 导出为字节数组

当我们需要导出为字节数组时,可以调用本方法。之后随你怎么加工。

    /*** 导出多个sheet到多个excel文件,并压缩到一个zip文件。最终得到一个字节数组。*/public static byte[] exportZip(List<ExcelData> excelDataList) {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();exportZipStream(excelDataList, byteArrayOutputStream);return byteArrayOutputStream.toByteArray();}

四、调试

4.1 构建调试用的实体类

指定简单的几个字段作为导出数据。

    @Data@AllArgsConstructorpublic static class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;}

4.2 控制器调用

组装假数据,进行导出。

@Controller
@RequestMapping("/excel")
public class ExcelDemoController {@GetMapping("/exportTransportDetail")public String exportTransportDetail(HttpServletResponse response) throws IOException {// 压缩包文件名String fileName = "结算单运单明细-" + System.currentTimeMillis() + ".zip";List<ExcelData> excelDataList = new ArrayList<>();// 第一个ExcelExcelData excelData1 = new ExcelData();excelData1.setFilename("结算单运单明细-1-" + System.currentTimeMillis() + ".xlsx");List<DemoData> demoData1 = new ArrayList<>();demoData1.add(new DemoData("excel-sheet1", new Date(), 123112.321));demoData1.add(new DemoData("excel-sheet1", new Date(), 34.3));List<DemoData> demoData2 = new ArrayList<>();demoData2.add(new DemoData("excel-sheet2", new Date(), 123112.321));demoData2.add(new DemoData("excel-sheet2", new Date(), 34.3));ExcelShellData<DemoData> shellData1 = new ExcelShellData<>(demoData1, "sheet1", DemoData.class);ExcelShellData<DemoData> shellData2 = new ExcelShellData<>(demoData2, "sheet2", DemoData.class);excelData1.addShellData(shellData1);excelData1.addShellData(shellData2);// 第2个ExcelExcelData excelData2 = new ExcelData();excelData2.setFilename("结算单运单明细-2-" + System.currentTimeMillis() +".xlsx");List<DemoData> demoData21 = new ArrayList<>();demoData21.add(new DemoData("excel-sheet21", new Date(), 123112.321));demoData21.add(new DemoData("excel-sheet22", new Date(), 34.3));List<DemoData> demoData22 = new ArrayList<>();demoData22.add(new DemoData("excel-sheet21", new Date(), 123112.321));demoData22.add(new DemoData("excel-sheet22", new Date(), 34.3));ExcelShellData<DemoData> shellData21 = new ExcelShellData<>(demoData21, "sheet1", DemoData.class);ExcelShellData<DemoData> shellData22 = new ExcelShellData<>(demoData22, "sheet2", DemoData.class);excelData2.addShellData(shellData21);excelData2.addShellData(shellData22);excelDataList.add(excelData1);excelDataList.add(excelData2);// 写法1///// exportZip(fileName, excelDataList , response);// 写法2///byte[] bytes = exportZip(excelDataList);response.setHeader("Content-disposition", "attachment;filename=" + fileName);response.setContentType("application/x-msdownload");response.setCharacterEncoding("utf-8");response.getOutputStream().write(bytes);response.getOutputStream().flush();return "succ";}
}

4.3 测试结果

可以看到压缩包解压后的效果:
在这里插入图片描述
其中一个文件内容如下:
sheet1:
在这里插入图片描述

sheet2:
在这里插入图片描述

五、注册大数转换器,长度大于15时,转换为字符串

5.1 实现转换器

/*** Excel 数值长度大于maxLength的数值转换为字符串*/public static class ExcelBigNumberConvert implements Converter<Long> {private final int maxLength;public ExcelBigNumberConvert() {this(15);}public ExcelBigNumberConvert(Integer maxLength) {this.maxLength = maxLength;}@Overridepublic Class<Long> supportJavaTypeKey() {return Long.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic Long convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {Object data = cellData.getData();if (data == null) {return null;}String s = String.valueOf(data);if (s.matches("^\\d+$")) {return Long.parseLong(s);}return null;}@Overridepublic CellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {if (object != null) {String str = object.toString();if (str.length() > maxLength) {return new CellData<>(str);}}return null;}}

5.2 使用转换器

在构建建造器时,增加注册转换器即可。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/208330.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rtmp 协议详解

1. handshake 1.1 概述 rtmp 连接从握手开始。它包含三个固定大小的块。客户端发送的三个块命名为 C0,C1,C2&#xff1b;服务端发送的三个块命名为 S0,S1,S2。 握手序列&#xff1a; 客户端通过发送 C0 和 C1 消息来启动握手过程。客户端必须接收到 S1 消息&#xff0c;然后…

阵列信号处理---频率-波数响应和波束方向图

波束延迟求和器 阵列是由一组全向阵元组成&#xff0c;阵元的位置为 p n p_n pn​&#xff0c;如下图所示&#xff1a; 阵元分别在对应的位置对信号进行空域采样&#xff0c;这样就产生了一组信号信号为 f ( t , p ) f(t,p) f(t,p),具体表示如下&#xff1a; f ( t , p ) [ f…

一些ab命令

1.ab简介 ab是apache自带的压力测试工具&#xff0c;是apachebench命令的缩写。ab非常实用&#xff0c;它不仅可以对apache服务器进行网站访问压力测试&#xff0c;也可以对或其它类型的服务器如nginx、tomcat、IIS等进行压力测试。 ab的原理&#xff1a;ab命令会创建多个并发…

锐捷EWEB网管系统 RCE漏洞复现

0x01 产品简介 锐捷网管系统是由北京锐捷数据时代科技有限公司开发的新一代基于云的网络管理软件&#xff0c;以“数据时代创新网管与信息安全”为口号&#xff0c;定位于终端安全、IT运营及企业服务化管理统一解决方案。 0x02 漏洞概述 Ruijie-EWEB 网管系统 flwo.control.ph…

Python开发——工具篇 Pycharm的相关配置,Python相关操作 持续更新

前言 本篇博客是python开发的工具篇相关&#xff0c;介绍pycharm的使用和相关配置&#xff0c;收录python的相关操作&#xff0c;比如如何启动jupyter。 目录 前言引出Pycharmpycharm如何不同等级日志显示不同颜色设置不同pycharm的python环境 Python操作如何启动Jupyter 总结…

计算机网络扫盲(2)——网络边缘

一、概述 在计算机网络得到术语中&#xff0c;我们把与因特网相连的计算机或其他设备称为端系统&#xff08;或者主机&#xff09;&#xff0c;如下图所示&#xff0c;因为它们位于因特网的边缘&#xff0c;所以被称为端系统。因特网的端系统包括了桌面计算机&#xff…

springboot集成邮箱验证功能

准备工作 开启SMTP服务 前往你的邮箱网站&#xff0c;以网易邮箱为例&#xff0c;打开网易邮箱地址&#xff0c;登录你的邮箱&#xff0c;进入邮箱管理后台界面。点击“设置”》》“POP3/SMTP/IMAP”后&#xff0c;点击开启SMTP服务即可。 技术实现 Spring Boot 发送邮件验证…

Edge 旧版本回退

微软官网 下载策略文件 下载后&#xff0c;解压打开 cad 包&#xff0c;把里面的 Windows\ADMX\ 下 3 个 *.admx 文件解压到 C:\Windows\PolicyDefinitions Windows\ADMX\zh-CN 下 3 个 *.adlm 文件解压到 C:\Windows\PolicyDefinitions\zh-CN Windows 搜索 gpedit&#xff…

使用MD5当做文件的唯一标识,这样安全么?

使用MD5作为文件唯一标识符可靠么&#xff1f; 文章目录 使用MD5作为文件唯一标识符可靠么&#xff1f;什么是MD5&#xff1f;MD5的用途MD5作为文件唯一标识的优劣优势劣势 使用MD5作为文件唯一标识的建议其他文件标识算法结束语 什么是MD5&#xff1f; MD5&#xff08;Messag…

C#:程序发布的大小控制

.net不讨喜有个大原因就是.net平台本身太大了&#xff0c;不同版本没有兼容性&#xff0c;程序依赖哪个版本用户就要安装哪个版本&#xff0c;除非你恰好用的是操作系统默认安装的版本——问题是不同版本操作系统默认安装的不一样。 所以打包程序就很头疼&#xff0c;不打包平台…

docker 安装jekins

echo Asia/Shanghai >/etc/timezone&#xff0c;容器中操作报错&#xff1a;docker容器中 Permission denied 使用该-u选项时&#xff0c;可以使用root用户(ID 0)&#xff0c;而不是用默认用户登录docker容器 docker exec -u 0 -it f8a2b3d91455 /bin/bash 或者&#xff…

Ext4文件系统解析(一)

1、前言 熟悉Linux操作系统的都应该或多或少的了解或者使用过Ext4文件系统。 接下来&#xff0c;会简单介绍Ext4文件系统的一些特性和工作原理。 2、常用概念 在介绍Ext文件系统之前&#xff0c;先简单描述一些相关概念。 块(Block)&#xff1a;Ext文件系统存储分配的基本单…

进程的创建:fork()

引入 创建进程的方式我们已经学习了一个&#xff01;在我们运行指令(或者运行我们自己写的可执行程序)的时候不就是创建了一个进程嘛&#xff1f;那个创建进程的方式称为指令级别的创建子进程&#xff01; 那如果我们想要在代码中创建进程该怎么办呢&#xff1f; fork() for…

Python离线下载torch与各种安装包

目的&#xff1a;在一个没有网络的win7电脑上部署python代码环境。 一、确定部署环境电脑上的python版本 我们需要明白win7上python版本最高为3.8.8&#xff0c;而很多安装包需要的python版本>3.8。所以&#xff1a; 3.8< python安装版本 <电脑支持的python的最高版…

canvas基础:绘制虚线

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 示例…

Redis数据存储:高效、灵活、实时

目录 引言 1. Redis概述 1.1 什么是Redis&#xff1f; 1.2 Redis的数据结构 1.3 Redis的持久化机制 2. Redis的使用场景 2.1 缓存 2.2 会话存储 2.3 发布/订阅系统 2.4 计数器和排行榜 3. Redis最佳实践 3.1 数据模型设计 3.2 键的命名规范 3.3 事务和原子操作 3…

Unittest自动化测试之unittestunittest_生成测试报告

unittest_生成测试报告 测试报告为测试结果的统计即展示&#xff0c;是自动化测试不可或缺的一部分&#xff0c;利用unittest 可以生成测试报告 方式一、使用第三方 HTMLTestRunner 执行测试用例集&#xff0c;生成网页版测试报告&#xff08;推荐&#xff09; HTMLTestRunn…

【C语言:数据在内存中的存储】

文章目录 1.整数在内存中的存储1.1整数在内存中的存储1.2整型提升 2.大小端字节序2.1什么是大小端2.2为什么有大小端之分 3.整数在内存中的存储相关题目题目一题目二题目三题目四题目五题目六题目七 4.浮点数在内存中的存储4.1浮点数存的过程4.2浮点数取得过程 在这之前呢&…

Android问题笔记四十九:ViewPager 嵌套 Fragment 扩大滑动响应区域,避免左右滑动过于灵敏问题

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

【Python】获取ip

要使用Python获取IP地址&#xff0c;可以使用socket库中的gethostname()函数和gethostbyname()函数。 import socketdef get_ip_address():hostname socket.gethostname()ip_address socket.gethostbyname(hostname)return ip_addressip get_ip_address() print("IP地…