Java操作Excel
导读
由于项目需要,需要在页面上实现导出功能,根据查询条件来导出。前端在搜索的表单中添加导出按钮。我这边提供对应的接口。
- 通过百度,发现以下几种方式
- Apache poi、EasyPOI
- 阿里的EasyExcel
- jxl
- JXLS
- 项目中用什么,不由我决定
JXLS介绍
- jxls是一个简单的、轻量级的excel导出库,使用特定的标记在excel模板文件中来定义输出格式和布局。
- 官网:http://jxls.sourceforge.net/
- excel excel文件的三个特性:
- 源文件(.xls) :Workbook 使用来获取工作薄对象需要使用的类.
- 表(sheet) : Sheet 接收对应的 Sheet 文件
- 单元格(cell):Cell 接收对应的Cell。
- exce操作:新建、读取和修改。
- 在使用jXLS时,你所需要做的就是在一个Excel模板中定义所有报表格式和数据布局,并运行jXLS引擎,为它提供数据以填充模板。在大多数情况下,你需要编写的唯一代码是使用适当配置的jXLS引擎的简单调用。
主要概念
Area(区域)
Area代表Excel文件中的矩形区域。可以使用单元格范围或使用开始单元格和区域大小(行和列数)定义矩形区域。Area包括特定范围的所有Excel单元格。
每个Area可以关联一组命令,jXLS引擎处理区域时执行和区域相关的命令。Area可以嵌套子区域。每个子区域也是一个Area,也可以包含自己的命令和自己的子区域。
Area可以使用以下方式定义:
- 在Excel模板文件中使用特定标记语法。
- 使用XML配置。
- 使用jXLS Java API。
Command(命令)
命令代表一个或多个Area的转换动作。Command接口如下所示:
1
2
3
4
5
6
7public interface Command {
String getName();
List<Area> getAreaList();
Command addArea(Area area);
Size applyAt(CellRef cellRef, Context context);
void reset();
}Command的主要方法是
Size applyAt(CellRef cellRef, Context context)
。- 该方法在单元格cellRef执行命令动作。
- context类似于一个map,用于为命令传递数据。
- 该方法返回转换后区域的大小作为Size对象。
当前,jXLS提供以下内置命令:
- Each
- If
- Image
Transformer
Transformer接口允许Area与特定Excel实现无关。这意味着通过提供不同的Transformer接口实现,我们可以使用不同的底层Java->Excel库。
Transformer接口如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public interface Transformer {
void transform(CellRef srcCellRef, CellRef targetCellRef, Context context, boolean updateRowHeight);
void setFormula(CellRef cellRef, String formulaString);
Set<CellData> getFormulaCells();
CellData getCellData(CellRef cellRef);
List<CellRef> getTargetCellRef(CellRef cellRef);
void resetTargetCellRefs();
void resetArea(AreaRef areaRef);
void clearCell(CellRef cellRef);
List<CellData> getCommentedCells();
void addImage(AreaRef areaRef, byte[] imageBytes, ImageType imageType);
void write() throws IOException;
TransformationConfig getTransformationConfig();
void setTransformationConfig(TransformationConfig transformationConfig);
boolean deleteSheet(String sheetName);
void setHidden(String sheetName, boolean hidden);
void updateRowHeight(String srcSheetName, int srcRowNum, String targetSheetName, int targetRowNum);
}尽管Transformer接口看起来有很多方法,但大多数方法已经在AbstractTransformer基础抽象类中实现,如果需要提供新Java->Excel实现,只需继承抽象类即可。
当前,jXLS提供两种Transformer接口实现:
- PoiTransformer
- JexcelTransformer
PoiTransformer使用Apache POI类库生成Excel文件。JexcelTransformer基于较老的Java Excel API类库。
Excel生成示例
Maven引入
jXLS对Apache POI和Java Excel API进行高级封装,因此,要使用jXLS必须引入底层Apache POI或Java Excel API,,Apache POI是Java开源社区最强Excel解决方案,选择使用Apache POI作为jXLS的底层API。
1
2
3
4
5
6
7<!-- 基于Java Excel API的转换器实现 -->
<!-- https://mvnrepository.com/artifact/org.jxls/jxls-jexcel -->
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-jexcel</artifactId>
<version>${jxls-jexcel.version}</version>
</dependency>完整的xml文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44<poi.version>4.1.2</poi.version>
<jxls.version>2.9.0</jxls.version>
<jxls-jexcel.version>1.0.9</jxls-jexcel.version>
<jxls-reader.version>2.0.6</jxls-reader.version>
<jxl.version>2.6.12</jxl.version>
<!-- 核心Jxls模块 -->
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls</artifactId>
<version>${jxls.version}</version>
</dependency>
<!-- Jxls转换引擎的实现 jXLS-POI适配器 -->
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-poi</artifactId>
<version>${jxls.version}</version>
</dependency>
<!--选择使用Apache POI作为jXLS的底层API -->
<!-- Apache POI依赖包 -->
<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-scratchpad</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-excelant</artifactId>
<version>${poi.version}</version>
</dependency>
准备模板
实体类
1
2
3
4
5
6
7
8
9
10
11
12public class Employee {
private String name;// 员工名称
private String sex;// 性别
private String telephone;// 手机号码
private Date birthday;// 生日
private BigDecimal payment;// 工资
// ... 构造函数
// ... getters/setters
}创建Excel模板
模板是使用特定标记规定jXLS应该如何输出数据的Excel文件。j
jXLS提供一些内置标记处理器解析Excel模板和提取控制命令。如有需要可以自定义标记处理器。
可以为Excel模板定义自己的标记,并以适当的方式进行解析,以创建jXLS命令结构。
默认,jXLS以Apache JEXL作为表达式语言在Excel模板中引用Java对象属性和方法。对象必须在jXLS上下文中某一键上可用。为了在单元格中输出员工名称,可以在单元格中放入${employee.name}。使用
${}
环绕JEXL表达式。假设在上下文中employee键下有一个Employee对象。模板中第三行的单元格使用JEXL表达式引用employee对象的属性。
单元格A1包含的Excel注释
jx:area(lastCell="E3")
定义模板根区域为A1:E3。单元格A3包含的Excel注释
jx:each(items="employees" var="employee" lastCell="E3")
定义jXLS Each命令。- Each命令迭代employees(由items属性定义)集合的条目到上下文的employee(由var属性定义)键。
- 执行Each命令的区域是A3:E3(有lastCell属性定义),该区域将会被克隆,并使用上下文中的每个新的Employee对象处理。
生成xls文件
使用API生成文件
1
2
3
4
5
6
7
8
9
10
11public static void main(String[] args) throws IOException {
List<Employee> data = new ArrayList<Employee>();
for (int i = 0; i < 4; i++) {
data.add(new Employee("张"+i,i%2==0?"男":"女","1300000000"+i, LocalDate.now(),new BigDecimal( i+"000")));
}
InputStream is = Application.class.getClassLoader().getResourceAsStream("employee_template.xls");
FileOutputStream os = new FileOutputStream("target/employee_output.xls");
Context context = new Context();
context.putVar("employees", data);
JxlsHelper.getInstance().processTemplate(is, os, context);
}在target目录下可以看到生产的文件,打开如下:
Area(区域)
- Area代表Excel文件中需要转换的一个矩形区域。每个Area有一组与之关联的转换命令和一组嵌套子区域。
- 每个子区域也是一个Area,有自己的一组命令和嵌套子区域。顶层的Area没有父区域(没有嵌套在任意其它Area中)。
- 构建Area的方式:jXLS使用AreaBuilder接口构建Area,jXLS提供了两个实现类:XmlAreaBuilder和XlsCommentAreaBuilder,这两种构建方式都是基于解析各自的配置使用Java API构建Area。
- XmlAreaBuilder:基于XML配置文件构建Area
- XlsCommentAreaBuilder(默认):基于Excel模板中的单元格注释构建Area
- 通过JxlsHelper.setAreaBuilder()方法切换构建Area的方式
使用Java API构建Area(推荐)
Area的实现类为XlsArea,XlsArea有以下构造函数:
1
2
3
4
5
6
7
8
9// 顶层Area:必须提供一个Transformer实例
public XlsArea(AreaRef areaRef, Transformer transformer);
public XlsArea(String areaRef, Transformer transformer);
//区域Area
public XlsArea(CellRef startCell, CellRef endCell, Transformer transformer);
public XlsArea(CellRef startCellRef, Size size, List<CommandData> commandDataList, Transformer transformer);
public XlsArea(CellRef startCellRef, Size size);
public XlsArea(CellRef startCellRef, Size size, Transformer transformer);示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 创建Transformer实例
// 创建顶级区域
XlsArea xlsArea = new XlsArea("Sheet1!A1:G15", transformer);
// 创建'department'区域
XlsArea departmentArea = new XlsArea("Sheet1!A2:G13", transformer);
// 创建'EachCommand'迭代departments
EachCommand departmentEachCommand = new EachCommand("department", "departments", departmentArea);
// 创建'employee'区域
XlsArea employeeArea = new XlsArea("Sheet1!A9:F9", transformer);
// 为'IfCommand'创建区域
XlsArea ifArea = new XlsArea("Sheet1!A18:F18", transformer);
// 使用指定区域创建'if'命令
IfCommand ifCommand = new IfCommand("employee.payment <= 2000", ifArea, new XlsArea("Sheet1!A9:F9", transformer));
// 添加'if'命令实例到employee区域
employeeArea.addCommand(new AreaRef("Sheet1!A9:F9"), ifCommand);
// 创建employee 'each'命令,添加到department区域
Command employeeEachCommand = new EachCommand( "employee", "department.staff", employeeArea);
departmentArea.addCommand(new AreaRef("Sheet1!A9:F9"), employeeEachCommand);
// 添加department 'each'命令到顶级区域
xlsArea.addCommand(new AreaRef("Sheet1!A2:F12"), departmentEachCommand);
使用Excel标记构建Area(常用)
标记放置在区域中的第一个单元格的Excel注释中
jx:area(lastCell = "<AREA_LAST_CELL>")
,<AREA_LAST_CELL>
定义区域的最后一个单元格引用为了解析标记并创建XlsArea对象,我们应该使用XlsCommentAreaBuilder类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 获取模板输入流
InputStream is = Application.class.getClassLoader().getResourceAsStream("employee_template.xls");
// 创建POI Workbook
Workbook workbook = WorkbookFactory.create(is);
// 使用POI Workbook创建PoiTransformer
PoiTransformer transformer = PoiTransformer.createTransformer(workbook);
// 创建XlsCommentAreaBuilder实例
AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
// 使用AreaBuilder构造一个Area列表
List<Area> xlsAreaList = areaBuilder.build();
// 获取主Area
Area xlsArea = xlsAreaList.get(0);
// jXLS API的JxlsHelper类使用XlsCommentAreaBuilder作为默认AreaBuilder实现
使用XML配置
创建XML配置文件定义Area,根元素是xls。然后列出大量顶级area。
- 使用each xml元素定义了each命令 ,使用ref属性关联每个命令相关的区域
- 在each命令中有一个嵌套区域参数
1
2
3
4
5
6
7
8<xls>
<!-- 顶级区域A1:E3 -->
<area ref="Sheet1!A1:E3">
<each items="employees" var="employee" ref="Sheet1!A1:E3">
<area ref="Sheet1!A1:E3"/>
</each>
</area>
</xls>
jXLS Excel标记
jXLS的Excel标记分为3种:
- Bean属性标记
- 区域标记
- 命令标记
jXLS提供XlsCommentAreaBuilder类从Excel单元格注释读取标记。XlsCommentAreaBuilder实现通用AreaBuilder接口。AreaBuilder接口如下所示:
1
2
3
4// 返回一个Area对象列表
public interface AreaBuilder {
List<Area> build();
}如果你想要定义自己的标记,可以创建自己的AreaBuilder实现,解释输入Excel模板或任意其它输入。
Bean属性标记
- jXLS使用Apache JEXL表达式语言处理Bean属性标记。在未来版本中,可以配置表达式语言引擎,以便在需要时可以用任何其他表达式引擎替换JEXL。
- 默认,jXLS期望JEXL表达式放在模板文件中的${和}之间。
- 例如,以下单元格内容
${department.chief.age} years
告诉jXLS, - 假设,在Context中有一个department对象有一个chief对象有一个age属性,使用JEXL计算
department.chief.age
。 - 如果表达式
department.getChief().getAge()
计算等于35,jXLS将放置35 years
在单元格中。
- 例如,以下单元格内容
区域标记
jXLS区域标记用于定义jXLS引擎处理的根XlsArea。XlsCommentAreaBuilder支持的Excel单元格注释区域定义语法:
jx:area(lastCell="<LAST_CELL>")
定义矩形区域右下角单元格。第一个单元格是定义Excel注释的单元格。因此,假设我们有一个注释 jx:area(lastCell="G12")
在单元格A1中,根区域将读取A1:G12
。读取所有区域到xlsAreaList,然后保存第一个区域到xlsArea变量:
1
2
3AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
List<Area> xlsAreaList = areaBuilder.build();
Area xlsArea = xlsAreaList.get(0);
命令标记
命令应该定义在XlsArea中。XlsCommentAreaBuilder接收以下命令符创建Excel单元格注释:
1
jx:<command_name>(attr1='val1' attr2='val2' ... attrN='valN' lastCell=<last_cell> areas=["<command_area1>", "<command_area2", ... "<command_areaN>"])
<command_name>
是在XlsCommentAreaBuilder中预注册或手动注册的命令名称。当前,预注册了以下命令:each,if,imageattr1, attr2,…, attrN是命令属性
。if命令有condition
属性设置条件表达式。<last_cell>
定义命令区域的右下角单元格。右上角由注释所在单元格决定。<command_area1>, <command_area2>, … <command_areaN>
——区域作为参数传递给命令。
使用XlsCommentAreaBuilder的
static void addCommandMapping(String commandName, Class clazz)
方法注册自定义命令。示例
显示工资小于等于2000的信息
Each命令
- 用于迭代集合,克隆命令XlsArea。类似于Java语言的for操作符。
命令属性
Each命令有以下属性:
var:迭代时,将集合中的每个条目放入Context中的那个键上。
items:要迭代的集合在Context中的键值。
area:Each命令体的XlsArea。
direction:Direction枚举,有效值是DOWN和RIGHT,表示如何(通过行或列)重复命令体。默认为DOWN。
- 默认,Each命令direction属性设置为DOWN表示命令体基于Excel行向下克隆。
- 如果你需要通过列克隆区域,应该设置属性值为RIGHT。
select:迭代过程中过滤集合条目的选择表达式。
groupBy:分组属性。
Each命令支持通过groupBy属性分组。groupOrder属性设置排序方式,有效值为desc或asc。
1
2
3
4
5
6
7jx:each(items="employees" var="myGroup" groupBy="name" groupOrder="asc" lastCell="D6")
# 每个组可以使用myGroup引用。
# 当前组条目可以使用myGroup.item引用。因此,可以使用以下方式引用员工姓名:
${myGroup.item.name}
# 通过items属性访问分组中的所有条目:
jx:each(items="myGroup.items" var="employee" lastCell="D6")
# 也可以跳过var属性,默认组变量名称为_group。
groupOrder:分组排序方式(‘desc’或‘asc’)。
cellRefGenerator:是否创建目标单元格引用的自定义策略。
multisheet:Context中sheet名称列表的键。
lastCell:命令指向的最后一个单元格。
var和items是必须的。
UpdateCell
- UpdateCell命令允许你自定义处理特定单元格。
命令属性
UpdateCell命令有以下属性:
- updater:Context中CellDataUpdater实现的键名。
- lastCell:命令体区域的最后一个单元格。
解释
CellDataUpdater接口如下所示:
- 转换UpdateCell命令区域之前,调用UpdateCellData方法传入当前的CellData,目标单元格和当前Context。
- 实现可以更新CellData来设置单元格值。
1
2
3public interface CellDataUpdater {
void updateCellData(CellData cellData, CellRef targetCell, Context context);
}例如,下面类更新统计公式:
- 关键代码是
cellData.setEvaluationResult(resultFormula)
,使用目标公式更新单元格数据。
1
2
3
4
5
6
7
8class TotalCellUpdater implements CellDataUpdater{
public void updateCellData(CellData cellData, CellRef targetCell, Context context) {
if( cellData.isFormulaCell() && cellData.getFormula().equals("SUM(E2)")){
String resultFormula = String.format("SUM(E2:E%d)", targetCell.getRow());
cellData.setEvaluationResult(resultFormula);
}
}
}- 关键代码是
为了在Excel模板中创建UpdateCell命令,创建一个单元格注释:
1
2
3# lastCell属性设置命令体区域最后一个单元格。
# updater属性设置为totalCellUpdater,处理之前,必须将totalCellUpdater实现加入Context。
jx:updateCell(lastCell="E4" updater="totalCellUpdater")1
2Context context = new Context();
context.putVar("totalCellUpdater", new TotalCellUpdater());
Grid命令
- 使用表头信息和数据行区域生成动态表格。
- 表头作为字符串集合传入,数据行作为对象集合或列表传入。
命令使用
Grid命令有以下属性:
- headers:表头集合键名。
- data:数据集合键名。
- props:逗号分隔的每个grid行的对象属性(如果grid行是对象时必须)。
- formatCells:逗号分隔的单元格类型映射信息,例如,formatCells=“Double:E1, Date:F1”。
- headerArea:源表头区域。
- bodyArea:源体区域。
- lastCell:命令体区域最后一个单元格。
data属性可以是以下两种类型:
Collection<Collection<Object>>
:每个内嵌Collection包含一行的一个单元格值。Collection<Object>
:每个Object包含一行。props用于设置单元格对应的Object属性。
Image命令
- 用于在Excel报表中输出图片。
用法
在想要输出图片的单元格上添加以下注释:
1
jx:image(lastCell="D10" src="image" imageType="PNG")
lastCell定义图片包含区域的右下角单元格。如果注释放置在A1单元格,那么图片将放在输出Excel的A1:D10区域。
src定义图片二进制在Context上的键。
imageType定义图片类型,可用值为:PNG、JPEG、EMF、WMF、PICT、DIB。
imageType属性的默认值为PNG。
Java代码应该包含这样的内容,以便将所需的图片放到Context中的image属性上:
1
2
3InputStream imageInputStream = ImageDemo.class.getResourceAsStream("business.png");
byte[] imageBytes = Util.toByteArray(imageInputStream);
context.putVar("image", imageBytes);
jXLS Reader读取
- jXLS提供jxls-reader模块,用于读取XLS文件并使用Excel数据填充Java Beans。
- XML配置文件用于表示如何解析Excel文件,如何填充数据。
Maven依赖
1 | <dependency> |
使用XML配置文件构建XLSReader
- 为了使用jXLS-Reader解析Excel文件,使用读取的数据填充Java数据,首先,你需要构建一个XLSReader对象。
- 最简单的方式使用特定XML配置文件构建XLSReader对象。
xml映射文件
xml文件的根元素为workbook,可以包含任意数量的子worksheet元素,worksheet标签应该包含name属性表示Excel Sheet的名称(我们的例子中为Sheet1)。
worksheet元素可以包含任意数量的section和loop子元素。
- section元素表示一个简单的单元格块。块的第一和最后一行使用startRow和endRow属性指定。
- 你必须为整个Excel Sheet指定section,因此,它完全被分割成多个section。这意味着,即使你不打算读取,例如第一行,也需要创建一个空section(section没有mapping,但指定startRow/endRow属性),以至于这些行在XML文件中体现。不需要的行将直接跳过,所有其他部分将按要求阅读。
XML单元格到Java Bean属性的映射使用mapping标签定义:
1
<mapping cell="B1">department.name</mapping>
你也可以使用cell属性指定映射的单元格,使用标签体作为完整属性名。所谓完整属性名,类似于department.name或department.chief.payment这种Java对象属性导航。其它单元格选项使用单元格行和列数(基于0)。
1
2# 这是定义单元格E4与属性department.chief.bonus的映射。
<mapping row="3" col="4">department.chief.bonus</mapping>loop元素定义Excel行循环块。该元素应该包含startRow和endRow属性指定循环块的开始和结束行。使用循环快数据填充context中的items属性名的集合。
- var属性指定在迭代内部section时如何引用该集合中的item。
- varTyp属性定义集合item的完整Java类名。
1
<loop startRow="7" endRow="7" items="department.staff" var="employee" varType="org.jxls.reader.sample.Employee">
loop元素可以包含任意数量的内部section和loop元素,并且必须包含loopbreakcondition定义。该标签标示中断循环的条件。在我们的例子中,该值为“Employee Payment Totals:”。
1
2
3
4
5
6<loopbreakcondition>
<!-- 循环到下一行为 “Employee Payment Totals:“的使用停止-->
<rowcheck offset="0">
<cellcheck offset="0">Employee Payment Totals:</cellcheck>
</rowcheck>
</loopbreakcondition>示例
下面是使用ReaderBuilder类使用XML映射文件和departmentdata.xls构建XLSReader类读取XLS数据填充相应Java Bean的代码:
xml文件如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<workbook>
<worksheet name="Sheet1">
<section startRow="0" endRow="6">
<mapping cell="B1">department.name</mapping>
<mapping cell="A4">department.chief.name</mapping>
<mapping cell="B4">department.chief.age</mapping>
<mapping cell="D4">department.chief.payment</mapping>
<mapping row="3" col="4">department.chief.bonus</mapping>
</section>
<loop startRow="7" endRow="7" items="department.staff" var="employee" varType="org.jxls.reader.sample.Employee">
<section startRow="7" endRow="7">
<mapping row="7" col="0">employee.name</mapping>
<mapping row="7" col="1">employee.age</mapping>
<mapping row="7" col="3">employee.payment</mapping>
<mapping row="7" col="4">employee.bonus</mapping>
</section>
<loopbreakcondition>
<rowcheck offset="0">
<cellcheck offset="0">Employee Payment Totals:</cellcheck>
</rowcheck>
</loopbreakcondition>
</loop>
</worksheet>
</workbook>Java代码如下
1
2
3
4
5
6
7
8
9
10
11InputStream inputXML = new BufferedInputStream(getClass().getResourceAsStream(xmlConfig));
XLSReader mainReader = ReaderBuilder.buildFromXML( inputXML );
InputStream inputXLS = new BufferedInputStream(getClass().getResourceAsStream(dataXLS));
Department department = new Department();
Department hrDepartment = new Department();
List departments = new ArrayList();
Map beans = new HashMap();
beans.put("department", department);
beans.put("hrDepartment", hrDepartment);
beans.put("departments", departments);
XLSReadStatus readStatus = mainReader.read( inputXLS, beans);
通过sheet索引映射
Jxls Reader支持通过sheet索引映射。
- 使用worksheet标签的idx属性替代name属性指定sheet索引。sheet索引从0开始。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<workbook>
<worksheet idx="0">
<section startRow="0" endRow="6">
<mapping cell="B1">department.name</mapping>
<mapping cell="A4">department.chief.name</mapping>
<mapping cell="B4">department.chief.age</mapping>
<mapping cell="D4">department.chief.payment</mapping>
<mapping row="3" col="4">department.chief.bonus</mapping>
</section>
<loop startRow="7" endRow="7" items="department.staff" var="employee" varType="org.jxls.reader.sample.Employee">
<section startRow="7" endRow="7">
<mapping row="7" col="0">employee.name</mapping>
<mapping row="7" col="1">employee.age</mapping>
<mapping row="7" col="3">employee.payment</mapping>
<mapping row="7" col="4">employee.bonus</mapping>
</section>
<loopbreakcondition>
<rowcheck offset="0">
<cellcheck offset="0">Employee Payment Totals:</cellcheck>
</rowcheck>
</loopbreakcondition>
</loop>
</worksheet>
</workbook>
错误处理
默认,如果jXLS读取某些单元格值失败将会停止进一步处理抛出XLSDataReadException。
你可以使用ReaderConfig类的setSkipErrors(true)方法覆盖该行为,允许跳过错误并继续处理。使用ReaderConfig.getInstance()方法获取ReaderConfig类的实例。
1
ReaderConfig.getInstance().setSkipErrors( true );
所有的错误消息存储在XLSReadStatus对象由read方法返回。XLSDataReadException可以包含该对象引用。我们可以使用getReadMessages()方法从XLSReadStatus分析XLSReadMessage对象。
转换机制
Jxls Reader继承Apache Commons Beanutils转换工具类执行Excel单元格值到Bean属性的实际转换。更多细节见ConvertUtil类。
Jxls Reader使用org.apache.commons.beanutils.converters包为原始类型标准转换器。J
xls Reader为java.util.Date类型提供自定义DataConverter,使用Apache POI工具方法从Excel数据表现形式转换为Date类。
BeanUtils转换器转换错误时为原始类型返回一个默认值。在ReaderConfig类中注册这些类抛出一个ConversionException,覆盖这些行为。
如果喜欢使用默认值,我们可以使用ReaderConfig类的setUseDefaultValuesForPrimitiveTypes(true)方法。例如:
1
ReaderConfig.getInstance().setUseDefaultValuesForPrimitiveTypes( true );
为了自定义转换器从Excel单元格到自定义属性类型,我们应该事先Converter接口,使用ConvertUtils类注册。
导出添加自定义函数
代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70public static void exportExcel(InputStream is, OutputStream os, Object model)
throws IOException {
JxlsHelper jxlsHelper = JxlsHelper.getInstance();
Context context = initJxlsContext(model);
// 必须要这个,否则表格函数统计会错乱
jxlsHelper.setUseFastFormulaProcessor(false);
Transformer transformer = jxlsHelper.createTransformer(is, os);
initFunctions(transformer);
jxlsHelper.processTemplate(context, transformer);
}
private static void initFunctions(Transformer transformer) {
// 设置静默模式,不报警告
// evaluator.getJexlEngine().setSilent(true);
// 函数强制,自定义功能
Map<String, Object> functionMap = new HashMap<String, Object>();
// 添加自定义功能
functionMap.put("dutils", new DateHelper()); // 添加自定义功能
functionMap.put("cutils", new CommonHelper()); // 添加自定义功能
// 获得配置
JexlExpressionEvaluator evaluator = new JexlExpressionEvaluator();
if (transformer == null) {
evaluator =
(JexlExpressionEvaluator) transformer.getTransformationConfig().getExpressionEvaluator();
}
evaluator.setJexlEngine(new JexlBuilder().namespaces(functionMap).create());
}
public class CommonHelper {
/**
*
* if判断,如果b为true,则返回o1,否则返回o2.
*
* @param b
* @param o1
* @param o2
* @return o1 or o2
*/
public Object ifelse(boolean b, Object o1, Object o2) {
return b ? o1 : o2;
}
}
public class DateHelper {
/**
*
* 日期格式化.
*
* @param date
* @param fmt
* @return 格式化后的字符串
*/
public String dateFmt(Date date, String fmt) {
if (date == null) {
return "";
}
try {
SimpleDateFormat dateFmt = new SimpleDateFormat(fmt);
return dateFmt.format(date);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}使用
1
2${dutils:dateFmt(user.createTime,"yyyy-MM-dd HH:mm:ss")}
${cutils:dateFmt(user.sex==0,"女","男")}