前言 芝麻乐开源众筹系统采用php+mysql开发,基于MVC开发,适用于各类互联网金融公司使用,程序具备模板分离技术,您可以根据您的需要进行应用扩展来达到更加强大功能。前端使用pintuer、jquery、layer等....系统易于使用和扩展简单的安装和升级向导多重业务逻辑判断,预防出现bug后台图表数据方式,一目了然后台包含但不限于以下功能:用户认证角色管理节点管理管理员管理上传配置支付配置短信平 最近有读者在问easypoi的问题,抽空整理了一份文章。 正文 EasyPOI功能如同名字Easy,主打的功能就是容易,让一个没接触过POI的人员可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出。通过简单的注解和模板语言(熟悉的表达式语法),完成以前复杂的写法。 本文主要通过简单的分析让读者知道Excel模板该如何编写,EasyPOI要如何使用才能导出满足自己需要的Excel数据,从而简化编码。同时本文还会对一些不常见的功能如图片导出功能进行说明,让读者少踩坑。 EasyPOI4.0.0及以后的版本依赖于Apache POI的4.0.0及以后版本。所以在maven的配置中,两者的版本号一定要匹配。 需要注意的是,Apache POI的4.0.0相对之前的版本有很大的变更,如果之前代码中Excel操作部分依赖于旧的版本,那么不建议使用4.0.0及之后的版本。当然,如果之前代码不涉及或很少涉及WorkBook的创建细节,使用新版也没有问题。 笔者需要改写的项目基于JEECG 3.7版本,依赖的是3.9版本的Apache POI,而JEECG维护的jeasypoi版本最高只有2.2.0,而该版本并不支持模板导出图片功能。说到这里又要吐槽以下JEECG团队,既然自己不打算维护jeasypoi,那项目中直接使用官方的EasyPOI不就好了,2.2.0版本的jeasypoi给开发者挖了多少坑啊! 为了和旧版本兼容,又想使用EasyPOI带来的图片导出功能,所以笔者最终采用的EasyPOI版本是3.3.0,对应的Apache POI依赖是3.15。 Maven配置如下所示: 我们使用EasyPOI的模板导出功能就是不想通过编码的方式来设计Excel报表的样式,所以工作的第一步就是设计Excel模板,分清楚哪些部分是固定的,哪些是需要循环填充的。EasyPOI有自己的表达式语言,每种表达式的详细介绍请参考后文的参考链接。 一些简单的模板就不在这里详细解释了,只放一下效果图和模板配置内容。等读者明白了复杂的模板如何制作并如何填值的时候,简单的很快就能明白了。 先看看报表效果图: 再看看实际的模板: 看了上述两张图,是不是已经感受到模板导出功能的强大了呢? 下面要介绍的这个模板比较复杂,不像是常见的那种一行是一条记录的情况,所以将详细介绍该模板的配置,顺带对EasyPOI的部分表达式进行简单介绍。 还是先看效果图: 再看看模板: 这两张图一对比,是不是有种知识改变命运的感觉? 从货品信息的模板图及效果图中我们发现,整个模板实际上分为上下两部分。其中上部分为不变的抬头信息,下部分为循环插入的货品明细信息。所以我们关注的重点是下半部分的语法。 下半部分的第一列图中没有显示完整,实际上是{{!fe: list t.id。 注意,这里 没有 }}符号!根据EasyPOI的官方文档,{{}}代表的是表达式,根据表达式取里边的值。仔细看图可以发现,表达式的闭合符号{{}}出现在图中的右下角。也就是说,从第一列{{开始至右下角}}结束,这中间的所有内容都是表达式的一部分。 因为整个模板信息都是表达式的一部分,所以即使是普通字符串也需要专门标明。下面对表达式中的子表达式进行逐个说明。 !fe: 遍历数据不创建row。 官方文档中的这句话大家理解起来可能有点费解,什么叫不创建row?实际上,不创建row是相对于创建row而言的,创建row的表达式是fe:。 就像是数据库中每条记录对应着一个实体对象,创建row表示每行就是一个实体对象Entity,这个实体对象的属性用{{}}表达式包裹起来。 不创建row表示整个表达式中只有一个实体对象Object,只不过这个Object比较特别,它是由list中N个Entity拼接起来的。每一个Entity不仅仅是指模型本身,也包含了Excel的样式,比如占用了几个单元格,单元格的坐标、排布顺序等。 list 自定义的名称,表示表达式中的数据集合,由代码以list为键,从Map list这个名字容易理解,就是一个占位符,可以随便取。EasyPOI解析到list就知道Map 搜索Java知音公众号,回复“后端面试”,送你一份Java面试题宝典.pdf t 预定义值,表示集合中的任意对象。 从模板中我们大致能感觉到,list中每个对象叫做t,t.name就代表t的name属性,所以t这个名字就可以随便叫,反正它和list一样,作用是占位符。 但实际上这是一个大坑!如果你把t换成了其他值比如g,模板中其他地方写g.name g.code等等,最终是解析不到的!官方文档对这一点并没有强调,而是作者实际踩了坑之后才发现的! ]] 换行符 多行遍历导出。 对于这个符号的官方解释也是莫名其妙,什么叫换行符,多行遍历导出?实际上它的意思就是,当解析到表达式中含有这个符号,该行后边的内容就不解析了,管你后边有没有其他内容或者样式。 该符号一定要写在每行的最后一列,不然会出现每行列数不一样的情况,EasyPOI内部做赋值的时候就会报空指针异常了。 ‘’ 单引号表示常量值 ‘’ 比如’1’ 那么输出的就是 1 官方文档中这里的介绍也有坑。''是表示常量值,但实际上Excel中只是这么些是不对的,因为Excel的单元格中遇到'后会认为后面都是字符串,所以得在单元格中写''库别:',这样显示出来的才是'库别:',而不是字符串库别:'。 经过上述分析,图中的模板实际上就类似以下内容: {{!fe: list t.id ‘库别:’ t.bin 换行 ‘商品名称:’ t.name 换行 ‘商品编号:’ t.code t.barcode 换行 ‘生产日期:’ t.proDate 换行 ‘进货日期:’ t.recvDate}} 如果list中有多条记录,上述字符串就再循环拼接一些内容,最终都在一个{{}}表达式中。 至此,模板的设计已剖析完毕,读者可根据自己的需求结合官方文档自行设计模板。下面将对模板赋值进行介绍。 从上节的描述中可知,只需要准备一个Map 上述代码中需要特殊关注的是图片导出部分。EasyPOI导出图片有两种方式,一种是通过图片的Url,还有一种是获取图片的 ImageEntity是EasyPOI内置的一个JavaBean,用于设定图片的宽度和高度、导出方式、RowSpan和ColumnSpan等。调试EasyPOI的源码可知,当设置了RowSpan或者ColumnSpan之后,图片的高度设置就失效了,图片大小会自动填充图片所在的单元格。 图片导出的坑点在于导出图片的大小。假设我们将四个单元格合成为一个,希望导出的图片能填充合并之后的单元格,但是对不起,EasyPOI暂时做不到,它只会填充合并之前左上角的单元格,具体原因如下源码所示: 可以看到,在创建图片插入位置的时候已经指定了图片的跨度为1行1列,即左上角的单元格。如果之前又设置了RowSpan或者ColumnSpan,那么图片高度的设置也会失效,最终导致导出的图片非常小。 搜索Java知音公众号,回复“后端面试”,送你一份Java面试题宝典.pdf 个人认为ImageEntity提供的RowSpan或者ColumnSpan的set方法并没有什么用,因为我们动态创建的合并单元格并不能被赋值。所以,导出图片的最好方式就是直接指定它的高度,因为宽度会自动填充单元格,模板中单元格的宽度要合适。 以上准备工作全部完成后就可以将模板和数据进行组装了,或者说是渲染,代码如下所示: 网上针对EasyPOI的介绍多限于最基本的行插入功能,但实际上Excel模板的需求可能各式各样。本文只是抛砖引玉,对EasyPOI中的部分概念做了详细介绍,希望帮助大家少踩坑。 如果想详细了解EasyPOI的各种功能,参考链接中的文档说明及测试项目源码就是最好的学习资料。希望大家都能得心应手地使用EasyPOI,大大提升开发效率! EasyPOI官方文档 EasyPOI测试项目 近日有网友求助我解决EasyPOI的复杂模板配置问题,通过解决该网友的问题发现了EasyPOI中的几个坑点,补充说明几个问题。 在复杂模板设计剖析一节中已经描述了EasyPOI支持的复杂的模板该如何配置。该模板的配置是绝对正确的,但是有3个点没有说清楚,大家在照葫芦画瓢时容易出错:
版本及依赖说明
<properties>
<poi.version>3.15</poi.version>
<easypoi.version>3.3.0</easypoi.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>${easypoi.version}</version>
</dependency>
</dependencies>
Excel模板的设计
一个简单的Excel报表模板


一个复杂的Excel报表模板


复杂模板设计剖析
准备模板数据
Map<String, Object> total = new HashMap<>();
List<Map<String, Object>> mapList = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
Map<String, Object> map = new HashMap<>();
map.put("id", i + "");
map.put("bin", "001 1000千克");
map.put("name", "商品" + i);
map.put("code", "goods" + i);
map.put("proDate", "2019-05-30");
map.put("recvDate", "2019-07-07");
// 插入图片
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
BufferedImage bufferImg = ImageIO.read(BarcodeUtil.generateToStream("001"));
ImageIO.write(bufferImg, "jpg", byteArrayOut);
ImageEntity imageEntity = new ImageEntity(byteArrayOut.toByteArray(), 200, 1000);
map.put("barcode", imageEntity);
mapList.add(map);
}
total.put("list", mapList);
图片数据导出
byte[],毕竟图片的本质就是byte[]。因为笔者的项目中图片不是存放在数据库之中,而是需要根据查询结果动态生成条码,所以通过byte[]导出图片。//BaseExportService.java
ClientAnchor anchor;
if (type.equals(ExcelType.HSSF)) {
anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
cell.getRow().getRowNum() + 1);
} else {
anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
cell.getRow().getRowNum() + 1);
}//ExcelExportOfTemplateUtil.java
if (img.getRowspan()>1 || img.getColspan() > 1){
img.setHeight(0);
PoiMergeCellUtil.addMergedRegion(cell.getSheet(),cell.getRowIndex(),
cell.getRowIndex() + img.getRowspan() - 1, cell.getColumnIndex(), cell.getColumnIndex() + img.getColspan() -1);
}
将数据导出至模板
public static void exportByTemplate(String templateName, Map<String, Object> data, OutputStream fileOut) {
TemplateExportParams params = new TemplateExportParams("export/template/" + templateName, true);
try {
Workbook workbook = ExcelExportUtil.exportExcel(params, data);
workbook.write(fileOut);
} catch (Exception e) {
LogUtil.error("", e);
}
}
总结
参考链接
一些坑
0
0
本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门AI工具
相关专题
本专题聚焦 Java 主流构建工具 Maven 的学习与应用,系统讲解项目结构、依赖管理、插件使用、生命周期与多模块项目配置。通过企业管理系统、Web 应用与微服务项目实战,帮助学员全面掌握 Maven 在 Java 项目构建与团队协作中的核心技能。
0
2025.09.15
在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。
910
2023.08.02
java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。
1561
2023.10.24
js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。
678
2023.08.03
js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。
219
2023.09.04
java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。
1561
2023.10.24
字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。
645
2023.11.24
Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。
1128
2024.03.22
本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。
3
2026.03.03
热门下载
相关下载
精品课程

