bootstraptable导出excel独立使用_使用 EasyPOI 优雅导出Excel模板数据(含图片)

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配置如下所示:

<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模板的设计

我们使用EasyPOI的模板导出功能就是不想通过编码的方式来设计Excel报表的样式,所以工作的第一步就是设计Excel模板,分清楚哪些部分是固定的,哪些是需要循环填充的。EasyPOI有自己的表达式语言,每种表达式的详细介绍请参考后文的参考链接。

一个简单的Excel报表模板

一些简单的模板就不在这里详细解释了,只放一下效果图和模板配置内容。等读者明白了复杂的模板如何制作并如何填值的时候,简单的很快就能明白了。

先看看报表效果图:

05001720f14bf9afe75095eabf7ffdb0.png

再看看实际的模板:

d434efb04ba9b0a68f8343277ffa580f.png

看了上述两张图,是不是已经感受到模板导出功能的强大了呢?

一个复杂的Excel报表模板

下面要介绍的这个模板比较复杂,不像是常见的那种一行是一条记录的情况,所以将详细介绍该模板的配置,顺带对EasyPOI的部分表达式进行简单介绍。

还是先看效果图:

79e1fd1f97241c82527fc57e5af73188.png

再看看模板:

7e6f9e3885fbc6e45c8da7f2d77a7f48.png

这两张图一对比,是不是有种知识改变命运的感觉?

复杂模板设计剖析

从货品信息的模板图及效果图中我们发现,整个模板实际上分为上下两部分。其中上部分为不变的抬头信息,下部分为循环插入的货品明细信息。所以我们关注的重点是下半部分的语法。

下半部分的第一列图中没有显示完整,实际上是{{!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<String, Object>中获取值的集合。

list这个名字容易理解,就是一个占位符,可以随便取。EasyPOI解析到list就知道Map<String, Object>中存在着该键的值的集合,后边解析到数据就从该集合中取即可。

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<String, Object>的对象即可,其中键为list,值为一个List数组,数组中元素类型为Map<String, Object>。代码如下:

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);

图片数据导出

上述代码中需要特殊关注的是图片导出部分。EasyPOI导出图片有两种方式,一种是通过图片的Url,还有一种是获取图片的byte[],毕竟图片的本质就是byte[]。因为笔者的项目中图片不是存放在数据库之中,而是需要根据查询结果动态生成条码,所以通过byte[]导出图片。

ImageEntity是EasyPOI内置的一个JavaBean,用于设定图片的宽度和高度、导出方式、RowSpan和ColumnSpan等。调试EasyPOI的源码可知,当设置了RowSpan或者ColumnSpan之后,图片的高度设置就失效了,图片大小会自动填充图片所在的单元格。

图片导出的坑点在于导出图片的大小。假设我们将四个单元格合成为一个,希望导出的图片能填充合并之后的单元格,但是对不起,EasyPOI暂时做不到,它只会填充合并之前左上角的单元格,具体原因如下源码所示:

//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);
}

可以看到,在创建图片插入位置的时候已经指定了图片的跨度为1行1列,即左上角的单元格。如果之前又设置了RowSpan或者ColumnSpan,那么图片高度的设置也会失效,最终导致导出的图片非常小。

个人认为ImageEntity提供的RowSpan或者ColumnSpan的set方法并没有什么用,因为我们动态创建的合并单元格并不能被赋值。所以,导出图片的最好方式就是直接指定它的高度,因为宽度会自动填充单元格,模板中单元格的宽度要合适。

//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);}
}

总结

网上针对EasyPOI的介绍多限于最基本的行插入功能,但实际上Excel模板的需求可能各式各样。本文只是抛砖引玉,对EasyPOI中的部分概念做了详细介绍,希望帮助大家少踩坑。

如果想详细了解EasyPOI的各种功能,参考链接中的文档说明及测试项目源码就是最好的学习资料。希望大家都能得心应手地使用EasyPOI,大大提升开发效率!

参考链接

EasyPOI官方文档

  • https://opensource.afterturn.cn/doc/easypoi.html

EasyPOI测试项目

  • https://gitee.com/lemur/easypoi-test

一些坑

近日有网友求助我解决EasyPOI的复杂模板配置问题,通过解决该网友的问题发现了EasyPOI中的几个坑点,补充说明几个问题。

在复杂模板设计剖析一节中已经描述了EasyPOI支持的复杂的模板该如何配置。该模板的配置是绝对正确的,但是有3个点没有说清楚,大家在照葫芦画瓢时容易出错:

  1. {{!fe: list需要在一个单独的列中。EasyPOI源码中是根据该单元格的行、列跨度来决定list中的每个元素需要多少行的。比如上述图片中,该单元格的跨度是5行1列,也就是说,以后list中的每个元素都会占用5行。如果觉得该列不符合自定义模板的风格,可以把该列的列宽设置为0,但一定需要有{{!fe: list。
  2. 在对象的起始和结束符号{{}}之间不能有任何空的单元格!代码中在解析到该单元格为空时会直接抛异常,如果就希望该单元格为空,得显示写入空字符串:’’’。
  3. 换行符]]必须占用每行的最后一个单元格!比如说第一行有10个单元格,第二行只用了前5个,那么不能直接在第5个结束直接写换行符]],而是需要把6-10个单元格合并,然后写入]]。参考上述图片中生产日期所在行的最后一列。这么设置的原因是EasyPOI要求每行的单元格数目完全一致,因为源码中判断了每个单元格的列跨度,如果提前使用了]]换行符,那么该列的数目就和其他行不同,那么赋值的时候就乱掉了,会出现索引异常。

来源:使用EasyPOI导出Excel模板数据(含图片)_星悬_月的博客-CSDN博客

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

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

相关文章

import pandas as pd什么意思_Pandas万花筒:让绘图变得更美观

全文共1803字&#xff0c;预计学习时长10分钟图源&#xff1a;tehrantimes流行 Python 数据分析库 Pandas 中的绘图功能一直是迅速绘制图表的首选之一。但是&#xff0c;其可用的可视化效果总是十分粗略&#xff0c;实用有余、美观不足。笔者常用 Pandas 的绘图功能快速地执行一…

jquery name选择器_jquery笔记

jQuery是一个封装好的特定的集合(方法和函数)。是一个库&#xff0c;封装了很多预先定义好的函数在里面。概念jQuery是一个快速&#xff0c;简洁的js库。j是js&#xff0c;query是查询&#xff0c;把js中的DOM操作作了封装&#xff0c;我们可以快速的查询使用里面的功能。jQuer…

python数据库框架_目前最受欢迎的12个Python开源框架

1 Django Django是一款用Python语言写的免费开源的 Python Web应用开发框架&#xff0c;它遵循模型 -视图-控制器(MVC)的架构模式。它是由非营利Django维护软件基金会(DSF)和3条款BSD许可下可用&#xff0c;鼓励快速发展和实用的设计。Django同时是一款在数据库功能、后台功能、…

jenkins java常用插件下载,Jenkins简单介绍以及插件入门

Jenkins是一个广泛用于持续构建的可视化web工具&#xff0c;就是各种项目的的“自动化”编译、打包、分发部署&#xff0c;将以前编译、打包、上传、部署到Tomcat中的过程交由Jenkins&#xff0c;Jenkins通过给定的代码地址&#xff0c;将代码拉取到jenkins宿主机上&#xff0c…

linux安装python3.7的步骤_Linux 安装python3.7.3

我这里使用的是Redhat6.5&#xff0c;centos系统本身默认安装有python2.x&#xff0c;版本x根据不同版本系统有所不同&#xff0c;可通过 python --V 或 python --version 查看系统自带的python版本 有一些系统命令时需要用到python2&#xff0c;不能卸载 1、安装依赖包 1&…

python画三维立体图难吗_万万没想到,Python竟能绘制出如此酷炫的三维图

作者 | Jay Alammar转载自 | 高级农民工 通常我们用 Python 绘制的都是二维平面图&#xff0c;但有时也需要绘制三维场景图&#xff0c;比如像下面这样的&#xff1a;这些图怎么做出来呢&#xff1f;今天就来分享下如何一步步绘制出三维矢量&#xff08;SVG&#xff09;图。 八…

php人物行走,非常震撼的纯CSS3人物行走动画

今天分享给大家的是一个用纯CSS3实现的人物行走动画&#xff0c;在没有使用JavaScript的情况下&#xff0c;用CSS3技术将人物行走的姿态描绘得非常逼真。其实动画实现的原理也是比较简单的&#xff0c;将人物行走时的状态分割成多张图片&#xff0c;然后利用CSS3的动画属性将这…

php jquery 弹窗提示框,jQuery实现消息弹出框效果

本文实例为大家分享了jQuery消息弹出框的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下效果图实现代码.showMessage {padding: 5px 10px;border-radius: 5px;position: fixed;top: 45%;left: 45%;color: #ffffff;}.showMessageSuccess {background-color: #00B7EE;}…

python定义字符串数组_python数组声明

广告关闭 腾讯云11.11云上盛惠 &#xff0c;精选热门产品助力上云&#xff0c;云服务器首年88元起&#xff0c;买的越多返的越多&#xff0c;最高返5000元&#xff01; 如何在python? 我无法在文档中找到对数组的任何引用... 我正在制作一个小程序&#xff0c;在静态数组上保存…

python函数定义中参数列表里的参数是_python函数定义中的参数说明

原博文 2020-05-23 07:50 − > 描述函数定义时 涉及到的 位置参数&#xff0c;默认参数&#xff0c;关键字参数&#xff0c;可变参数等的概念及用法 ## 1 示例代码#! /usr/bin/env python # -*- coding: UTF-8 -*- import logging def get_default_logger(): log... 相关推荐…

我的世界手机有PHP的开服器下载,【BDS】MC基岩版官方开服器Windows版插件开发包...

您尚未登录&#xff0c;立即登录享受更好的浏览体验&#xff01;您需要 登录 才可以下载或查看&#xff0c;没有帐号&#xff1f;注册(register)xMC基岩版官方开服器Windows版插件开发教程2019年5月22日 作者&#xff1a;Player前言&#xff1a;MC基岩版官方开服器(BDS)自发布至…

c++ 协程_用yield实现协程

上一篇 理解python中的yield关键字 介绍了使用yeld实现生成器函数&#xff0c;这一篇我们来继续深入的了解一下yield&#xff0c;用yield实现协程。先来解答一下上一篇留下的问题&#xff1a;下面的代码为什么第二次调用next打印None呢&#xff1f;def 事实是这样的&#xff0c…

pandas 第一行_用Excel表格带你学习pandas最核心的处理操作,不再害怕条件统计

此系列文章收录在公众号中&#xff1a;数据大宇宙 > 数据处理 >E-pd转发本文并私信我"python"&#xff0c;即可获得Python资料以及更多系列文章(持续更新的)经常听别人说 Python 在数据领域有多厉害&#xff0c;结果学了很长时间&#xff0c;连数据处理都麻烦得…

网页载入动画 php,网站页面加载动画代码

有时候网站服务器带宽不好或者用户网络不好&#xff0c;网站速度打开就会比较慢&#xff0c;那么用户等待就会比较无聊&#xff0c;下面准备了一个loading页面&#xff0c;可以在网站文件未完全加载完毕之前展示。HTML部分&#xff0c;放在body标签下面(最好是最靠近body)正在加…

ehcache缓存原理_贼厉害,手撸的 SpringBoot缓存系统,性能杠杠的!

缓存是最直接有效提升系统性能的手段之一。个人认为用好用对缓存是优秀程序员的必备基本素质。本文结合实际开发经验&#xff0c;从简单概念原理和代码入手&#xff0c;一步一步搭建一个简单的二级缓存系统。一、通用缓存接口1、缓存基础算法FIFO&#xff08;First In First Ou…

angular 字符串转换成数字_Angular日期在TypeScript中格式化转换应用

组件模板中我们常常使用Angular内置管道DatePipe将其日期格式化&#xff0c;如同{{ startTime | date:yyyy-MM-dd }}这般操作&#xff0c;即可转换为字符串格式。那么在TS文件中我们也可以通过DatePipe或formatDate方法将其格式化。Angular日期在TypeScript中格式化转换应用​w…

python罗马数字转换,Python3.5实现的罗马数字转换成整数功能示例

本文实例讲述了Python3.5实现的罗马数字转换成整数功能。分享给大家供大家参考&#xff0c;具体如下&#xff1a;问题概述&#xff1a;给定一个罗马数字 &#xff0c;将罗马数字转换成整数。如罗马数字I&#xff0c;II&#xff0c;III&#xff0c;IV&#xff0c;V分别代表数字 …

js在一个指定元素前添加内容_WebAR开发指南(1)---使用AR.js实现第一个WebAR demo...

前面有一篇文章万字干货介绍WebAR的实现与应用 分析了目前流行的WebAR框架并简单的介绍一些实现方法&#xff0c;这个专栏我们具体的来通过一些框架实现WebAR效果。关于AR.jsAR.js是一个轻量级的增强现实类JavaScript库&#xff0c;支持基于标记和位置的增强现实。开发人员可以…

python 最小二乘回归 高斯核_从简单数学建模开始:08最小二乘准则的应用(附python代码)...

模型拟合一般来说有这么三种&#xff1a;切比雪夫近似准则极小化绝对偏差之和最小二乘准则这几个原则各有各的适用范围。其中最小二乘准则是比较容易计算的。接下来我将简要的介绍最小二乘准则以及举例说明如何用python实现。最小二乘准则定义&#xff1a;给定某种函数类型 和 …

oracle 时间集合,oracle 日期函数集合(集中版本)第2/2页

oracle 日期函数集合(集中版本)第2/2页更新时间&#xff1a;2009年06月16日 23:45:55 作者&#xff1a;oracle 日期函数网上已经有了不少&#xff0c;特我们跟集中一下&#xff0c;免得大家麻烦。一、 常用日期数据格式1.Y或YY或YYY 年的最后一位&#xff0c;两位或三位SQL&g…