当前位置: 首页 > news >正文

Springboot功能模块之EasyExcel

一、EasyExcel简介

1.1 程序简介

​ Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称。

easyExcel能大大减少内存占用的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

内存问题:POI = 100w先加载到内存 OOM。。再写入文件 而 easyExcel是一行一行的完成

EasyExcel的GitHub地址:GitHub - alibaba/easyexcel: 快速、简洁、解决大文件内存溢出的java处理Excel工具

EasyExcel的官方文档:EasyExcel(文档已经迁移) · 语雀

1.2 Excel格式分析

  • xls是Microsoft Excel2007前excel的文件存储格式,实现原理是基于微软的ole db是微软com组件的一种实现,本质上也是一个微型数据库,由于微软的东西很多不开源,另外也已经被淘汰,了解它的细节意义不大,底层的编程都是基于微软的com组件去开发的。
  • xlsx是Microsoft Excel2007后excel的文件存储格式,实现是基于openXml和zip技术。这种存储简单,安全传输方便,同时处理数据也变的简单。
  • csv 我们可以理解为纯文本文件,可以被excel打开。他的格式非常简单,解析起来和解析文本文件一样。

二、快速入门

EasyExcel所需的依赖:

 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.71</version></dependency>

我们在引入EasyExcel依赖之后,我们需要注意下面的问题,因为EasyExcel里面已经集成了很多的依赖,并且里面就包含了POI的依赖:

2.1写入Excel

首先我们需要创建一个实体类.用来映射到我们在Excel中将要填充的对象

@Data
public class DemoData {@ColumnWidth(20)@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")private Date date;@ExcelProperty("数字标题")@NumberFormat("#.##")private Double doubleData;/*** 忽略这个字段*/@ExcelIgnoreprivate String ignore;
}

报错解决

https://ask.csdn.net/questions/7703338

属性注解 

@ExcelProperty(""):用来标注Excel中字段的标题

@ExcelIgnore:用来表示该字段忽略,不用添加到Excel中

@DateTimeFormat 日期格式转换

@NumberFormat数值格式转换

 样式注解

@ColumnWidth设置某一列单元格宽度,如20

@ContentFontStyle 、@HeadFontStyle设置某一单元格字体样式

//字体
String fontName() default "";
//字体大小(short) 10
short fontHeightInPoints() default -1;
//斜体
boolean italic() default false;
//删除线
boolean strikeout() default false;
//字体颜色,在如下类中查找
//org.apache.poi.ss.usermodel.Font.COLOR_NORMAL
//org.apache.poi.ss.usermodel.Font.COLOR_RED
//org.apache.poi.hssf.util.HSSFColor
//org.apache.poi.ss.usermodel.IndexedColors
short color() default -1;
short typeOffset() default -1;
//下划线
//Font#U_NONE
//Font#U_SINGLE
//Font#U_DOUBLE
//Font#U_SINGLE_ACCOUNTING
//Font#U_DOUBLE_ACCOUNTING
byte underline() default -1;
//字符集
//FontCharset
//Font#ANSI_CHARSET
//Font#DEFAULT_CHARSET
//Font#SYMBOL_CHARSET
int charset() default -1;
//加粗
boolean bold() default false;
//示例
@ContentFontStyle(fontName = "微软雅黑",fontHeightInPoints = 20,italic = true,strikeout = true,color = 10,bold = true)
private String name;

@ContentRowHeight 所有数据行的行高

short value() default -1;
@ContentRowHeight(40)
//示例
public class User {
}

@ContentStyle 、@HeadStyle 设置内容行单元格的样式

//poi的dataformat功能,参数见BuiltinFormats类
short dataFormat() default -1;
// 是否隐藏
boolean hidden() default false;
// 是否锁定
boolean locked() default false;
//quotePrefix
boolean quotePrefix() default false;
//水平居中
HorizontalAlignment horizontalAlignment() default HorizontalAlignment.GENERAL;
//包装
boolean wrapped() default false;
//垂直居中
VerticalAlignment verticalAlignment() default VerticalAlignment.CENTER;
//内容旋转角度,value 0-180
short rotation() default -1;
//边框
BorderStyle borderLeft() default BorderStyle.NONE;
BorderStyle borderRight() default BorderStyle.NONE;
BorderStyle borderTop() default BorderStyle.NONE;
BorderStyle borderBottom() default BorderStyle.NONE;
//边框颜色
short leftBorderColor() default -1;
short rightBorderColor() default -1;
short topBorderColor() default -1;
short bottomBorderColor() default -1;
//设置前景色填充类型 org.apache.poi.ss.usermodel.FillPatternType
//要修改Excel的背景色必须同时设置以下两个值
//@ContentStyle(fillForegroundColor = 10,fillPatternType = FillPatternType.SOLID_FOREGROUND)
FillPatternType fillPatternType() default FillPatternType.NO_FILL;
//前景色 org.apache.poi.ss.usermodel.IndexedColors
short fillForegroundColor() default -1;
//背景色(基本不用) org.apache.poi.ss.usermodel.IndexedColors
short fillBackgroundColor() default -1;
//控制单元格是否应自动调整内容大小,以适应文本过长的情况
boolean shrinkToFit() default false;

@HeadRowHeight 标题行的行高

short value() default -1;
@HeadRowHeight(40)
public class User {
}

这样我们的数据写入就完成了,运行代码之后我们就可以看到已经在我们的项目路径下生成了easyexcel文件了

简单读

    private static  String PATH = System.getProperty("user.dir") + java.io.File.separator + "files";private List<DemoData> data() {List<DemoData> list = new ArrayList<DemoData>();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();data.setString("字符串" + i);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;}/*** 最简单的写* <p>1. 创建excel对应的实体对象 参照{@link DemoData}* <p>2. 直接写即可*/@Testpublic void simpleWrite() {// 写法1
//        String fileName = PATH+ java.io.File.separator+ "easyexcel.xlsx";
//        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
//        // 如果这里想使用03 则 传入excelType参数即可
//        EasyExcel.write(fileName, DemoData.class)
//                .sheet("模板")
//                .doWrite(data());// 写法2String   fileName = PATH+ java.io.File.separator+ "easyexcel.xlsx";// 这里 需要指定写用哪个class去写ExcelWriter excelWriter = null;try {excelWriter = EasyExcel.write(fileName, DemoData.class).build();WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();excelWriter.write(data(), writeSheet);} finally {// 千万别忘记finish 会帮忙关闭流if (excelWriter != null) {excelWriter.finish();}}}

更多

写示例

2.2读取Excel

首先我们需要创建一个监听器:

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);/*** 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 5;List<DemoData> list = new ArrayList<DemoData>();/*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/private DemoDAO demoDAO;public DemoDataListener() {// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数demoDAO = new DemoDAO();}/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来** @param demoDAO*/public DemoDataListener(DemoDAO demoDAO) {this.demoDAO = demoDAO;}/*** 这个每一条数据解析都会来调用** @param data*            one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {System.out.println(JSON.toJSONString(data));LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));list.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (list.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listlist.clear();}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();LOGGER.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {LOGGER.info("{}条数据,开始存储数据库!", list.size());demoDAO.save(list);LOGGER.info("存储数据库成功!");}
}

之后我们需要根据自己的需要创建一个DAO功能其实就类似于我们的service层,可以在这里面定义我们后来可能加入的与数据库的相关操作的方法

/*** 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。**/
public class DemoDAO {public void save(List<DemoData> list) {// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入}
}

创建完成之后我们的功能基本就可以了,之后就可以进行测试了:

/*** 最简单的读* <p>1. 创建excel对应的实体对象 参照{@link DemoData}* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}* <p>3. 直接读即可*/@Testpublic void simpleRead() {// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去// 写法1:String fileName = PATH+ "easyexcel.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();//        // 写法2:
//        String fileName = PATH+ "easyexcel.xlsx";
//        ExcelReader excelReader = null;
//        try {
//            excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
//            ReadSheet readSheet = EasyExcel.readSheet(0).build();
//            excelReader.read(readSheet);
//        } finally {
//            if (excelReader != null) {
//                // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
//                excelReader.finish();
//            }
//        }}

具体

写案例

2.3 填充Excel模板

EasyExcel自2.1.1后提供了更强大的表格填充功能,提供一个模板文件,写一些模板参数,就可以快速生成

编写模板 -> 传入模板绝对路径 -> 渲染数据

(1)模板

将Excel实体类的属性用{.属性名}填入模板文件,样式也会被复制。注意:填充List时需要带个点,如果单个就不需要。如下:

姓名年龄其他
{.name}{.age}要想显示大括号可用反斜杠转译\ {

(2)API:填充列表

//API与写入Excel相同,多一项填充
public ExcelWriterBuilder withTemplate(InputStream templateInputStream) {}
public ExcelWriterBuilder withTemplate(File templateFile) {}
public ExcelWriterBuilder withTemplate(String pathName) {}
public void doFill(Object data) {}
public void doFill(Object data, FillConfig fillConfig) {}
//示例
EasyExcel.write(response.getOutputStream()).withTemplate(templateFilePath).sheet().doFill(list)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Vue3+Vite 解决“找不到模块“@/components/xxx.vue”或其相应的类型声明 ts(2307)”
  • 【Git】更新拉取Coding子仓库代码 及 过程中用户名密码输什么 git submodule
  • 做其他的不好吗?别写代码了(3)
  • 深度解读AI大模型最火的智能体(AI Agent)
  • CRMEB 多店版 v3.1更新预告,快来尝鲜
  • 大数据-95 Spark 集群 SparkSQL Action与Transformation操作 详细解释与测试案例
  • 百度最新旗舰大模型文心4.0 Turbo精调服务上线
  • flutter_markdown 基本使用
  • C语言分析数据在内存中的存储一:(整形在内存中的存储)
  • 手动修改zk类型的kafka offset
  • Django 中render、redirect 和 HttpResponse的区别
  • QPushbutton checked状态下文字显示不全
  • 学懂C++(三十六):深入理解与实现C++进程间通信(IPC)
  • Controller中接收数组参数 post请求中在body中传+post请求中通过表单形式传(x-www-form-urlencoded)
  • Python接口自动化测试框架介绍
  • [nginx文档翻译系列] 控制nginx
  • Angular4 模板式表单用法以及验证
  • angular组件开发
  • Django 博客开发教程 16 - 统计文章阅读量
  • Hibernate最全面试题
  • nginx 负载服务器优化
  • Spring Cloud中负载均衡器概览
  • webpack+react项目初体验——记录我的webpack环境配置
  • 彻底搞懂浏览器Event-loop
  • 当SetTimeout遇到了字符串
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 如何编写一个可升级的智能合约
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 在Mac OS X上安装 Ruby运行环境
  • 追踪解析 FutureTask 源码
  • 我们雇佣了一只大猴子...
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • # Redis 入门到精通(八)-- 服务器配置-redis.conf配置与高级数据类型
  • #、%和$符号在OGNL表达式中经常出现
  • #QT项目实战(天气预报)
  • #Ubuntu(修改root信息)
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (2.2w字)前端单元测试之Jest详解篇
  • (3)选择元素——(17)练习(Exercises)
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (多级缓存)多级缓存
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (学习日记)2024.02.29:UCOSIII第二节
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .net core 依赖注入的基本用发
  • .NET Core中Emit的使用