开发者社区> meijm0103> 正文

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
19322 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
28597 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
13343 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
22307 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
15992 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
20427 0
+关注
meijm0103
知其然,知其所以然
62
文章
6
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载