JXLS 2.4.0学习

简介:

LXLS 2.4.0 简介

jxls2.4 是java对excel操作的工具包,可以很方便的实用模板输出excel,根据xml配置读取excel内容。
对比jxls1 现在的版本使用批注的模式运行指令,可以支持excel中使用公式,07的excel。官网
使用:

  • maven中添加依赖
<dependency>
    <groupId>org.jxls</groupId>
    <artifactId>jxls</artifactId>
    <version>2.4.5</version>
</dependency>
<dependency>
<!-- 可以使用poi的实现也可以用jexcelapi的 -->
    <groupId>org.jxls</groupId>
    <artifactId>jxls-poi</artifactId>
    <version>1.0.15</version>
</dependency>
<dependency>
    <groupId>org.jxls</groupId>
    <artifactId>jxls-jexcel</artifactId>
    <version>1.0.7</version>
</dependency>

导出EXCEL

基础介绍

  • 使用批注的模式编写指令
  • 需在excel的第一个单元格指定整个模板的范围
  • 单元格中取值与el表达式类似使用${}来访问传入的变量
  • 指令中需要访问对象则不需要使用${} 直接访问即可,需用""包裹
  • 官方demo

常用指令说明:

jx:each 循环

示例:jx:each((items="employees" var="employee" lastCell="D4" area="[A4:D4]" select="employee.payment > 2000"))
参数说明:

参数名称 示例 必填 说明
items items="employees" 必填 循环的集合对象
var var="employee" 必填 循环中的变量名,指定之后区域内可以使用该名称访问属性
lastCell lastCell="D4" 必填 指令对应的结束位置
direction direction ="RIGHT"   输出的方向向下(DOWN)或向右(RIGHT),默认为DOWN
area areas=["A8:F8","A13:F13"]   循环的区域,一次循环多行则需要填写,可以指定多个区域使用逗号分隔
select select="employee.payment>2000"   过滤条件,不需要通过${}来取值
groupBy groupBy ="name"   jx:each(items="employees" var="nameGroup" groupBy="name" groupOrder="asc" lastCell="D7")依据employee中name进行分组,分组后的集合可通过nameGroup.items来获取,官网示例中后面还有一个each指定的items就是nameGroup.items
groupOrder groupOrder ="asc"   指定分组排序(‘desc’ or ‘asc’)
multisheet multisheet ="sheets"   循环的sheet名称集合,指定后会产生多个sheet,指定后each的维度会变为sheet,不需要指定area
shiftMode shiftMode ="adjacent"   adjacent指定后通过添加行的方式向指定方向输出,inner:则为通过添加单元格的方式向指定方向输出,默认为inner
cellRefGenerator ?   ?

不指定shiftMode ="adjacent"的效果

jx:if 条件判断

示例:jx:if(condition="department.chief.name != 'Betsy' " lastCell="F4" areas=["A3:F4"])

参数名称 示例 必填 说明
condition condition="department.chief.name != 'Betsy' " 必填 判断条件,字符串不需要通过${}来取值,直接访问传递对象的内容
ifArea  ifArea =["A3:F4"]   if指令影响的范围 condition结果为true则显示指定范围
elseArea elseArea=["A3:F4"]   if指令影响的范围 condition结果为false则显示指定范围

jx:updateCell 动态输出公式

说明:主要用于动态修改模板中被循环包裹的的公式,demo中的相关例子在org.jxls.demo.SxssfDemo中
示例:jx:updateCell(lastCell="E4" updater="totalCellUpdater")

参数名称 示例 必填 说明
updater updater="totalCellUpdater" 必填 指定具体的操作类需实现CellDataUpdater接口

java代码示例:

    static class TotalCellUpdater implements CellDataUpdater{
        /**
        * cellData 批注对应的单元格
        * targetCell 输出的单元格
        * context  模板中的上下文通过getVar(变量名)来获取传入的对象
        */
        @Override
        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);
            }
        }
    }

jx:grid 输出一个表格

说明:一次性输出一个表格包含表头表体,demo中的相关例子在org.jxls.demo.GridCommandDemo中
示例:jx:grid(lastCell="A4" headers="headers" data="data" areas=[A3:A3, A4:A4] formatCells="BigDecimal:C1,Date:D1")

参数名称 示例 必填 说明
headers headers="headers" 必填 循环的表头内容为List ,表头和表体没有必然的关系,可以多一列也可以少一列
data data="data" 必填 循环的表体内容为List/List,当为javabean时java代码中需指定读取的属性名称
formatCells formatCells="BigDecimal:C1,Date:D1"   指定单元格格式化方式


java代码示例:

        try(InputStream is = GridCommandDemo.class.getResourceAsStream("grid_template.xls")) {
            try(OutputStream os = new FileOutputStream("grid_output2.xls")) {
                Context context = new Context();
                context.putVar("headers", Arrays.asList("Name", "Birthday", "Payment"));
                context.putVar("data", employees);
                //当循环的表体为javabean时指定读取的属性,Sheet2!A1表示输出开始的位置
                JxlsHelper.getInstance().processGridTemplateAtCell(is, os, context, "name,birthDate,payment,bonus", "Sheet2!A1");
            }
        }

jx:image 输出图片

说明:输出一张图片,demo中的相关例子在org.jxls.demo.ImageDemo中
示例:jx:image(lastCell="D10" src="image" imageType="PNG")

参数名称 示例 必填 说明
src src="image" 必填 输出的图片数据源byte[]
imageType imageType="PNG"   输出的图片格式可不填

java代码示例:

    public static void execute2() throws IOException {
        try(InputStream is = ImageDemo.class.getResourceAsStream(template2)) {
            try (OutputStream os = new FileOutputStream(output2)) {
                Context context = new Context();
                InputStream imageInputStream = ImageDemo.class.getResourceAsStream("business.png");
                byte[] imageBytes = Util.toByteArray(imageInputStream);
                Department department = new Department("Test Department");
                department.setImage(imageBytes);
                context.putVar("dep", department);
                JxlsHelper.getInstance().processTemplate(is, os, context);
            }
        }
    }

EXCEL中使用公式

  • 使用 来 使 用 中 的 公 式 例 如 [ ] 来 使 用 e x c e l 中 的 公 式 例 如 " [B4(1+${bonus})]"输出后为"=B6(1+0.1)",可以使用excel函数如SUM如选择某一列只需指定变量对应单元格,输出会自动替换为该列,如下图

    如公式中不需要访问传入属性直接写=SUM(B4)编译后也会自动替换为=SUM(B4:B8)

  • 支持java手动增加公式详细例子请看demo中org.jxls.demo.FormulaExportDemo

自定义

函数和指令都可以进行扩展,需在导出前进行初始化,原文顺便吐槽一下百度用标题查居然第一个出来的是转发的第三个才是原文。

具体的内容可以参考大神写的这个项目jxlss

函数

函数:在单元格中使用处理具体数据

   JxlsHelper jxlsHelper = JxlsHelper.getInstance();
   Transformer transformer = jxlsHelper.createTransformer(is, os);
   JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator) transformer.getTransformationConfig().getExpressionEvaluator();
   Map<String, Object> functions = new HashMap<>(1);
   //添加自定义功能
   functions.put("util", new JxlsUtil());//此处util为函数前缀,调用时在单元格中使用${util:函数名(参数)}来使用其中参数为当前上下文内容
   evaluator.getJexlEngine().setFunctions(functions);
   jxlsHelper.processTemplate(context, transformer);
   is.close();
//JxlsUtil 格式化工具类
public class JxlsUtil {
 /**
  * 日期格式化
  *
  * @param date
  * @param fmt
  * @return
  */
 public String dateFmt(Date date, String fmt) {
  if (date == null) {
   return null;
  }
  try {
   SimpleDateFormat dateFmt = new SimpleDateFormat(fmt);
   return dateFmt.format(date);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }
 /**
  * 返回第一个不为空的对象
  *
  * @param objs
  * @return
  */
 public Object defaultIfNull(Object... objs) {
  for (Object o : objs) {
   if (o != null) {
    return o;
   }
  }
  return null;
 }
 /**
  * if判断
  *
  * @param b
  * @param o1
  * @param o2
  * @return
  */
 public Object ifelse(boolean b, Object o1, Object o2) {
  return b ? o1 : o2;
 }
}

指令

指令:在批注中使用,可扩展原有jxls中的功能例如循环增加下标.
继承AbstractCommand

//扩展指令
public class ExEachCommand extends AbstractCommand {
//处理内容
public Size applyAt(CellRef cellRef, Context context) {}
....
}
//在具体导出的类中增加静态代码块,由于只需要执行一次所以用静态代码块比较合适
 static {
  //添加自定义指令(可覆盖jxls原指令)
  XlsCommentAreaBuilder.addCommandMapping("each",ExEachCommand.class);
 }

读取Excel

通过一个xml来对应excel中内容,读取时传入相应xml和excel来转换为javabean,
转换代码:

  try (InputStream xmlInputStream = EquipmentController.class.getResourceAsStream("/excelTemp/equipment_impot.xml")) {
   File tempfile = fileService.getFile(data, fileType, file);
   XLSReader reader = ReaderBuilder.buildFromXML(xmlInputStream);
   try (InputStream xlsInputStream = new FileInputStream(tempfile)) {
    List<Equipment> equipments = new ArrayList<>();
    Map<String, Object> beans = new HashMap<>(15);
    Map<String, String> title = new HashMap<>(3);
    beans.put("equipments", equipments);
    beans.put("title", title);
    //碰到数据类型转换异常时跳过异常,例如数字类型但是单元格中为空,默认是会抛出异常的.设置之后就会设置为null
    ReaderConfig.getInstance().setSkipErrors(true);
//从excel中读取对象,名称需和xml中对应
    reader.read(xlsInputStream, beans);
    if (checkTitle(title)) {
     message = equipmentService.importEquipment(equipments, firmInfo);
    } else {
     throw new BusinessAjaxException(TEMPLET_ERROR);
    }
    if (StringUtils.isBlank(message)) {
     message = SUCCESS_CONTENT;
    } else {
     data.put("status", HttpStatus.UNPROCESSABLE_ENTITY.value());
    }
    data.put("message", message);
   }
  } catch (BusinessAjaxException e) {
   throw e;
  } catch (XLSDataReadException e) {
   throw new BusinessAjaxException(String.format(JXLS_ERROR, e.getCellName()), e);
  } catch (Exception e) {
   throw new BusinessAjaxException(IMPORT_ERROR, e);
  }

对应xml文件:

<?xml version="1.0" encoding="ISO-8859-1"?>
<workbook>
    <worksheet name="Sheet1"><!--工作表名称,名称对应不上会读取不到数据,一次要读取多个sheet则重复此标签即可-->
        <section startRow="0" endRow="0"/>
        <section startRow="1" endRow="1">
            <mapping row="1" col="1">title.eqCode</mapping>
            <mapping row="1" col="7">title.origin</mapping>
            <mapping row="1" col="13">title.remark</mapping>
        </section>
        <loop startRow="2" endRow="2" items="equipments" var="equipment" varType="com.bmw.frame.entity.firm.Equipment">
            <section startRow="2" endRow="2">
                <mapping row="2" col="1">equipment.eqCode</mapping>
                <mapping row="2" col="2">equipment.eqName</mapping>
                <mapping row="2" col="3">equipment.brand</mapping>
                <mapping row="2" col="4">equipment.importBuyTime</mapping>
                <mapping row="2" col="5">equipment.importSpecification</mapping>
                <mapping row="2" col="6">equipment.price</mapping>
                <mapping row="2" col="7">equipment.origin</mapping>
                <mapping row="2" col="8">equipment.quantity</mapping>
                <mapping row="2" col="9">equipment.process</mapping>
                <mapping row="2" col="10">equipment.numberUsers</mapping>
                <mapping row="2" col="11">equipment.equipmentTrip</mapping>
                <mapping row="2" col="12">equipment.eqPrecision</mapping>
                <mapping row="2" col="13">equipment.remark</mapping>
            </section>
            <loopbreakcondition><!--退出读取的标识,可以指定结束读取的关键字,这里用的是空白-->
                <rowcheck offset="0">
                    <cellcheck offset="0"></cellcheck>
                </rowcheck>
            </loopbreakcondition>
        </loop>
    </worksheet>
</workbook>

注意事项:

  • 读取的数据类型为日期时数据转换很麻烦,所以我在这里是增加了一个字符串属性用于存储读取的内容,然后在java中进行格式转换,如有更好的解决方案望告知
  • 如属性中包含非字符串属性,excel中又可以不填需增加设置:ReaderConfig.getInstance().setSkipErrors(true)避免中止导入
  • 最好手动捕获XLSDataReadException 异常,e.getCellName()可以拿到具体导入报错的单元格,能精确定位

参考资料:

http://jxls.sourceforge.net/
https://blog.csdn.net/lnktoking/article/details/52932679
https://gitee.com/lnkToKing/jxlss
https://www.cnblogs.com/foxlee1024/p/7616987.html
https://blog.csdn.net/lnkToKing/article/details/52932679

目录
相关文章
|
11月前
|
程序员
学习学习再学习
学习学习再学习
83 0
|
8月前
|
数据安全/隐私保护
实用分享-学习资料下载器
实用分享-学习资料下载器
|
10月前
|
机器学习/深度学习 设计模式 算法
|
11月前
|
NoSQL Java jenkins
|
设计模式 安全 Java
【鸟瞰】C#的学习
前言: 在软件工程之C/S学习的过程中,我们已经学习过了软件工程,文档,九种UML图。下一个学习小阶段是C#和设计模式,视频里的老师上来就讲“.NET”,还说应该念成“dot Net”,念成“点NET”实在是太不专业了。我突然有点蒙圈了,为啥在这个阶段要学习C#?学C#为啥还和“dot Net”有关?怎么这么多C?什么C语言?C ++?C#?这些都是些什么鬼?晕!!! 于是开始在培养计划中寻找答案。。。
|
XML 监控 Dubbo
pmq再学习二
首先启动的过程中,会去获取消费组中的配置信息,拿到消费组中的配置信息后,执行注册消费组操作,而执行注册消费组操作中,会首先注册消费者,然后执行消费组操作,然后执行启动消费者轮询服务,执行mq检查服务启动,mq提交服务启动。完成后,执行监控服务配置操作。 这里面最为重要的是启动长轮询服务操作。因为长轮询服务涉及到执行重平衡操作和执行更新元数据操作。更新元数据操作涉及到更新队列元数据操作,此时不可避免的涉及到对偏移量的更新操作。
105 2
pmq再学习二
|
弹性计算 Linux 数据安全/隐私保护
学习
学习
|
弹性计算 Linux 虚拟化
选择正确,不断学习
对于学计算机的,对于我的专业,学习并掌握Linux操作系统是必须的,但是一开始在自己的电脑用VMware在自己的电脑搭建虚拟机学习,但是这样会导致自己的计算机变得很卡,因为会占用主机很大的内存。在我的老师的引荐下,认识了阿里云服务器,而且他推荐我们去参加“飞天加速计划,高校学生在家实践活动”,那样可以先体验阿里云服务器ECS,看看是否适合自己。于是我便去完成了练习和答题拿到了体验资格。
|
存储 弹性计算 负载均衡
学习资料
个人记录学习资料
|
存储 缓存 网络协议
学习总结
学习总结

热门文章

最新文章