关于java对于报表工具的应用开发,我们今天来了解一下jxls;
jxls是一个简单的、轻量级的excel导出库,使用特定的标记在excel模板文件中来定义输出格式和布局。
其实java中成熟的excel导出工具有pol、jxl,但他们都是使用java代码的方式来导出excel,编码效率很低且不方便维护。
jxls是通过预制excel模板,然后通过jxls相应API将我们应用程序的数据结合模板格式输出到相应的excel文件中,从而形成报表;
因此,我们首先需要了解jxls模板的相关使用标记;下面以jxls 2.4.0的示例来说明:
excel模板示例:
Excel模板标记在jxls中的作用分为三部分:
- bean属性标记
- XLS Area定义标记
- XLS Command表示标记
bean属性标记
jxls使用 Apache JEXL表达式语言来解析定义在excel模板中的表达式。JEXL与JSTL相似,并对JSTL进行了扩展。eg:
${department.chief.age} //属性可以是无限深度
${utils:dateFmt(date,"yyyy-MM-dd")} //自定义工具函数
XLS Area定义标记
XLS Area 是JxlsPlus中的一个重要概念,它代表excel模板中需要被解析的矩形区域,由A1到最后一个单元格表示,有利于加快解析速度。
XLS Area 使用excel注释标注表示,它需要被定义在excel 模板的第一个单元格(A1):
jx:area(lastCell = "<AREA_LAST_CELL>")
这个标记定义了excel模板需要被解析的矩形区域为:A1到<AREA_LAST_CELL>。
XLS Command表示标记
XLS Command 使用excel注释标注表示,命令格式如下:
jx:<command_name>(attr1='val1' attr2='val2' ... attrN='valN' lastCell=<last_cell> areas=["<command_area1>", "<command_area2",
... "<command_areaN>"])
<command_name> 是库自带的命名或是用户自定义并注册到XlsCommentAreaBuilder的命令。
each 命令是最常用的XLS命令,形如:
jx:each(items="employees" var="employee" lastCell="D4")
each 可以有如下一些属性:
- items 上下文中集合的变量名;
- var 在遍历集合的时候每一条记录的变量名;
- area 该XLS Command的解析区域;
- direction 数据在excel中填充的方向,默认(DOWN)向下;
- select 其值为一个表达式,用来过滤数据。
我们下面来看一个简单例子:
以下代码中,分别实现了通过设置excel模板,java后台处理数据对Map的加载、对List<javabean>的加载,闲话不多说,直接上示例代码
javabean Emp类
public class Emp { private String id; private String name; private String position; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } }
Test测试类
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.jxls.area.Area; import org.jxls.builder.AreaBuilder; import org.jxls.builder.xls.XlsCommentAreaBuilder; import org.jxls.common.CellRef; import org.jxls.common.Context; import org.jxls.transform.Transformer; import org.jxls.util.TransformerFactory; import java.io.*; import java.sql.SQLException; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 简单示例 */ public class Test { @SuppressWarnings({ "unchecked", "rawtypes" }) public static void execute(FileOutputStream outputStream) throws IOException, InvalidFormatException, ParseException, SQLException { String template = "/xlstemplate/object_collection_template.xls";//报表模板 InputStream is = Test.class.getResourceAsStream(template); OutputStream os = outputStream; Transformer transformer = TransformerFactory.createTransformer(is, os); AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer); List<Area> xlsAreaList = areaBuilder.build(); Area xlsArea = xlsAreaList.get(0); Context context = new Context(); Calendar calendar = new GregorianCalendar(); calendar.setTime(new Date()); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH) + 1; int day = calendar.get(Calendar.DAY_OF_MONTH); String monthStr = month < 10 ? ("0"+month) : month+""; String date = year + monthStr + day; //简单报表(Map);basicform也可以是javabean形式 Map<String, Object> basicform = new HashMap(); basicform.put("dwmc", "测试"); basicform.put("zgbm", "测试11"); basicform.put("yyzzh", "测试22"); basicform.put("lxdh", "测试33"); basicform.put("slsj1", "测试44"); basicform.put("pdate", date); context.putVar("basicform", basicform); xlsArea.applyAt(new CellRef("封面!A1"), context); Area xlsArea1 = xlsAreaList.get(1); xlsArea1.applyAt(new CellRef("基本情况1!A1"), context); // List<Object>报表 Area xlsArea2 = xlsAreaList.get(2); List<Emp> lisEmp = new ArrayList<Emp>(); Emp e1 = new Emp(); e1.setId("1"); e1.setName("李X"); e1.setPosition("施工员"); lisEmp.add(e1); Emp e2 = new Emp(); e2.setId("2"); e2.setName("刘X"); e2.setPosition("监理"); lisEmp.add(e2); context.putVar("lisEmp", lisEmp); // Map<String, Object> emp = new HashMap(); // emp.put("name", "李XX"); // emp.put("id", "1"); // emp.put("position", "施工员"); // context.putVar("emp", emp); xlsArea2.applyAt(new CellRef("基本情况2!A1"), context); transformer.write(); is.close(); } public static void main(String[] args) throws IOException, SQLException, InvalidFormatException, ParseException { execute(new FileOutputStream(new File("d:/xxx.xls"))); } }
jexl自定义工具函数
如果你需要自定jexl来处理数据,你可以从Transformer对象获取JexlEngine引用,并对其配置。
下面的例子实现了将一个自定义jexl函数注册到utils命名空间下:
JxlsHelper jxlsHelper = JxlsHelper.getInstance(); Transformer transformer = jxlsHelper.createTransformer(is, os); JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator)transformer.getTransformationConfig().getExpressionEvaluator(); Map<String, Object> funcs = new HashMap<String, Object>(); funcs.put("utils", new JxlsUtils()); //添加自定义功能 evaluator.getJexlEngine().setFunctions(funcs);
demo
Employee.java
private String id; private String name; private Integer age; public Person(String id, String name, Integer age) { super(); this.id = id; this.name = name; this.age = age; } public Person() { } ....set,get 省略
建立excel模板:
JxlsUtils工具类:
package test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.jxls.common.Context; import org.jxls.expression.JexlExpressionEvaluator; import org.jxls.transform.Transformer; import org.jxls.transform.poi.PoiTransformer; import org.jxls.util.JxlsHelper; public class JxlsUtils{ public static void exportExcel(InputStream is, OutputStream os, Map<String, Object> model) throws IOException{ Context context = PoiTransformer.createInitialContext(); if (model != null) { for (String key : model.keySet()) { context.putVar(key, model.get(key)); } } JxlsHelper jxlsHelper = JxlsHelper.getInstance(); Transformer transformer = jxlsHelper.createTransformer(is, os); //获得配置 JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator)transformer.getTransformationConfig().getExpressionEvaluator(); //设置静默模式,不报警告 evaluator.getJexlEngine().setSilent(true); //函数强制,自定义功能 Map<String, Object> funcs = new HashMap<String, Object>(); funcs.put("utils", new JxlsUtils()); //添加自定义功能 evaluator.getJexlEngine().setFunctions(funcs); //必须要这个,否者表格函数统计会错乱 jxlsHelper.setUseFastFormulaProcessor(false).processTemplate(context, transformer); //并没什么卵用 //jxlsHelper.setDeleteTemplateSheet(true); } public static void exportExcel(File xls, File out, Map<String, Object> model) throws FileNotFoundException, IOException { exportExcel(new FileInputStream(xls), new FileOutputStream(out), model); } public static void exportExcel(String templatePath, OutputStream os, Map<String, Object> model) throws Exception { File template = getTemplate(templatePath); if(template != null){ exportExcel(new FileInputStream(template), os, model); } else { throw new Exception("Excel 模板未找到。"); } } //获取jxls模版文件 public static File getTemplate(String path){ File template = new File(path); if(template.exists()){ return template; } return null; } // 日期格式化 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 ""; } // if判断 public Object ifelse(boolean b, Object o1, Object o2) { return b ? o1 : o2; } }
Test测试类:
// 模板位置,输出流 String templatePath = "/xlstemplate/template_2.xls";// /xlstemplate/template2.xls templatePath = TestMain.class.getResource(templatePath).getPath(); OutputStream os = new FileOutputStream("E:/out.xls"); // 一个装有对象数据的链表 List<Person> persons = new ArrayList<Person>(); Person p1 = new Person("001", "张三", 18); Person p2 = new Person("002", "李四", 19); Person p3 = new Person("003", "王五", 20); persons.add(p1); persons.add(p2); persons.add(p3); Map<String, Object> model = new HashMap<String, Object>(); model.put("person", persons); // 把链表放进model中 JxlsUtils.exportExcel(templatePath, os, model); os.close(); System.out.println("完成");
运行截图:
