User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Java   ›   正文
Java

JXLS—Excel模板框架

2023-04-23 18:30:36
1632  0 1
参考目录 隐藏
1) 基础介绍
2) 常用指令说明
3) jx:each 循环
4) jx:if 条件判断
5) jx:updateCell 动态输出公式
6) jx:grid 输出一个表格
7) jx:image 输出图片
8) 自定义 Jexl 处理
9) 案例
10) execl标识
11) Java API 填充
12) API 工具封装

阅读完需:约 11 分钟

因为业务的需要,要解决报表样式的复杂度问题,每一个中国式报表的复杂度都是极其变态的,所以可以通过模版的方式来导出Excel样式,而不是通过代码来构造Excel的样式,这就是不采用原生的POI与EasyExcel等框架的原因。

官方文档:https://jxls.sourceforge.net/index.html

maven中添加依赖

<dependency>
    <groupId>org.jxls</groupId>
    <artifactId>jxls</artifactId>
    <version>2.12.0</version>
</dependency>

<dependency>
    <groupId>org.jxls</groupId>
    <artifactId>jxls-poi</artifactId>
    <version>2.12.0</version>
</dependency>

<!-- 用来导出excel用的-->
<dependency>
    <groupId>net.sf.jxls</groupId>
    <artifactId>jxls-core</artifactId>
    <version>1.0.6</version>
</dependency>

<!-- 用于读取内容 xml读取用的依赖 -->
<dependency>
    <groupId>org.jxls</groupId>
    <artifactId>jxls-reader</artifactId>
    <version>2.0.6</version>
</dependency>

除了对核心 Jxls 模块的依赖之外,我们需要添加对 Jxls 转换器引擎实现的依赖,该引擎将执行所有底层 Java 到 Excel 操作。

Jxls 核心模块不依赖于任何特定的 Java-Excel 库,而是通过预定义的接口专门与 Excel 一起工作。目前,Jxls 基于众所周知的Apache POI和Java Excel API 库,在不同的模块中提供了该接口的两种实现。

其中最关键的是Transformers接口,是通过这个来转换获取不同的实现。

基础介绍

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

在JXLS中最关键的是要制定填充的 XLS 区域,一共有三种方式来指定 XLS 区域

XLS 区域:分顶级区域,填充区域

有 3 种方法来构建 XLS 区域

  • 使用 Excel 标记
  • 使用 XML 配置
  • 使用 Java API

这里更只说明Excel 标记与使用 Java API,更倾向于Java API

常用指令说明

jx:each 循环

jx:each((items="employees" var="employee" lastCell="D4" area="[A4:D4]" select="employee.payment > 2000"))

Java API

// creating a transformer and departments collection
    ...
// creating department XlsArea
    XlsArea departmentArea = new XlsArea("Template!A2:G13", transformer);
// creating Each Command to iterate departments collection and attach to it "departmentArea"
    EachCommand departmentEachCommand = new EachCommand("department", "departments", departmentArea);

每个命令具有以下属性:

  • items是包含要迭代的集合 (Iterable<?>) 或数组的上下文变量的名称
  • var是 Jxls 上下文中变量的名称,用于在迭代时放置每个新的集合项
  • varIndex是 Jxls 上下文中的变量名称,它保存当前迭代索引,从零开始
  • direction是Direction枚举的值,它可能具有值DOWN或RIGHT以指示如何重复命令主体 – 按行或按列。默认值为向下。
  • select是一个表达式选择器,用于在迭代过程中过滤掉集合项
  • groupBy是一个进行分组的属性(在 var 名称前加上“.”)
  • groupOrder指示组的排序(’desc’ 或 ‘asc’)
  • orderBy包含以逗号分隔的属性名称,每个属性名称都有一个可选的后缀“ASC”(默认)或“DESC”用于排序顺序。您应该在 var 名称前加上“.” 在每个属性名称之前。
  • multisheet是上下文变量的名称,其中包含要输出集合的工作表名称列表
  • cellRefGenerator是用于创建目标单元格引用的自定义策略
  • area是对用作每个命令主体的 XLS 区域的引用
  • lastCell是指向命令区域最后一个单元格的任何命令的公共属性

var和items属性是必需的,而其他属性可以跳过。

循环变量var和varIndex的值将使用特殊方法 Context.getRunVar() 保存。这允许您在值不可用时单独做出反应。

jx:if 条件判断

jx:if(condition="employee.payment <= 2000", lastCell="F9", areas=["A9:F9","A18:F18"])

Java API

// ...
// creating 'if' and 'else' areas
XlsArea ifArea = new XlsArea("Template!A18:F18", transformer);
XlsArea elseArea = new XlsArea("Template!A9:F9", transformer);
// creating 'if' command
IfCommand ifCommand = new IfCommand("employee.payment <= 2000", ifArea, elseArea);

If -Command具有以下属性

  • condition是要测试的条件表达式
  • ifArea是当此命令条件计算为真时要输出的区域的引用
  • elseArea是当此命令条件计算为false时要输出的区域的引用
  • lastCell是指向命令区域最后一个单元格的任何命令的公共属性

ifArea和condition属性是必需的。

jx:updateCell 动态输出公式

主要用于动态修改模板中被循环包裹的的公式

jx:updateCell(lastCell="E4"  updater="totalCellUpdater")

UpdateCell -Command具有以下属性

  • updater是包含CellDataUpdater实现的上下文中的键的名称
  • lastCell是指向命令区域最后一个单元格的任何命令的公共属性
    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 输出一个表格

一次性输出一个表格包含表头表体

Grid-Command具有以下属性

  • headers – 包含标题集合的上下文变量的名称
  • data – 包含数据集合的上下文变量的名称
  • 道具- 每个网格行的逗号分隔的对象属性列表(仅当每个网格行都是对象时才需要)
  • formatCells – 类型格式地图单元格的逗号分隔列表,例如 formatCells=“Double:E1, Date:F1”
  • headerArea – 标题的源 xls 区域
  • bodyArea – 正文的源 xls 区域
  • lastCell是指向命令区域最后一个单元格的任何命令的公共属性

数据变量可以是以下类型

  1. Collection<Collection<Object>> – 这里每个内部集合都包含对应行的单元格值
  2. Collection<Object> – 这里每个集合项都是一个包含相应行数据的对象。在这种情况下,您必须指定props属性来定义应该使用哪些对象属性来设置特定单元格的数据。
        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 输出图片

输出一张图片

jx:image(lastCell="D10" src="image" imageType="PNG")
参数名称 示例 必填 说明
src src=”image” 必填 输出的图片数据源byte[]
imageType imageType=”PNG” 输出的图片格式可不填
    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);
            }
        }
    }

自定义 Jexl 处理

如果需要自定义 Jexl 处理,您可以从Transformer获取对JexlEngine的引用并应用必要的配置。

例如下面的代码在demo命名空间下注册了一个自定义的 Jexl 函数

public Integer mySum(Integer x, Integer y){
    return x + y;
}
     Transformer transformer = TransformerFactory.createTransformer(is, os);
     ...
     JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator) transformer.getTransformationConfig().getExpressionEvaluator();
     Map<String, Object> functionMap = new HashMap<>();
     functionMap.put("demo", new JexlCustomFunctionDemo());
     evaluator.getJexlEngine().setFunctions(functionMap);
${demo:mySum(x,y)}

案例

execl标识

    @Test
    public void jxls2() throws IOException {
        ArrayList<MultipartData> multipartData = selectSQL.selectResultSet(selectSQL1 -> "SELECT * FROM htpk.report_forms_input_output_one WHERE enterprise_id=57 and year=2022 and month=3");
        try(InputStream is = new FileInputStream("/Users/xxxx/Me/IDEA/Necrozma/Zacian-888/src/main/java/com/enmalvi/zacian888/controller/JxlsExcel/one2.xls")){
            try (OutputStream os = new FileOutputStream("target/one2-02.xls")) {
                Context context = new Context();
                context.putVar("tests", multipartData);
                JxlsHelper.getInstance().processTemplateAtCell(is, os, context,"Result!A1");
            }
        }
    }

这种方式是通过Jxls的transformer接口来访问实现这个transformer接口的转换类,比如如果底层是POI那么后面实际调用的是PoiTransformer transformer = createTransformer(is);

或者可以直接调用POI的转换类来进行

// getting input stream for our report template file from classpath
InputStream is = ObjectCollectionDemo.class.getResourceAsStream("object_collection_template.xls");
// creating POI Workbook
Workbook workbook = WorkbookFactory.create(is);
// creating JxlsPlus transformer for the workbook
PoiTransformer transformer = PoiTransformer.createTransformer(workbook);
// creating XlsCommentAreaBuilder instance
AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
// using area builder to construct a list of processing areas
List<Area> xlsAreaList = areaBuilder.build();
// getting the main area from the list
Area xlsArea = xlsAreaList.get(0);

关键在于

PoiTransformer transformer = PoiTransformer.createTransformer(workbook);
AreaBuilder areaBuilder = new XlsCommentAreaBuilder(transformer);
List<Area> xlsAreaList = areaBuilder.build();

Java API 填充

    @Test
    public void jxlsApiME() throws ParseException, IOException {
        ArrayList<MultipartData> employees = selectSQL.selectResultSet(selectSQL1 -> "SELECT * FROM htpk.report_forms_input_output_one WHERE enterprise_id=57 and year=2022 and month=3");
        log.info("Running Object Collection JavaAPI demo");
        try (InputStream is = new FileInputStream("/Users/xxxxx/Me/IDEA/Necrozma/Zacian-888/src/main/java/com/enmalvi/zacian888/controller/JxlsExcel/one.xls")) {
            try (OutputStream os = new FileOutputStream("target/reportFormsInputOutputOne.xls")) {
                // 转换地址
                Transformer transformer = TransformerFactory.createTransformer(is, os);
                // 创建一个新的 XlsArea,指定模板文件的区域(顶级区域)
                XlsArea xlsArea = new XlsArea("Template!A1:I44", transformer);
                // 数据填充区域
                XlsArea employeeArea = new XlsArea("Template!A2:I43", transformer);
                // 数据循环填充
                EachCommand employeeEachCommand = new EachCommand("reportFormsInputOutputOne", "employees", employeeArea);
                // 添加区域到顶级区域中
                xlsArea.addCommand("A2:I43", employeeEachCommand);
                Context context = new Context();
                // 设置数据集
                context.putVar("employees", employees);
                // 最后需要输出到那个标签页面里,从哪个单元格开始写入
                xlsArea.applyAt(new CellRef("Result!A1"), context);
                transformer.write();
            }
        }
    }

最后结果

API 工具封装

@RestController
public class ExcelUtil {

    @Autowired
    SelectSQL selectSQL;

    /**
     * 出口
     *
     * @param templatePath   模板路径
     * @param map            地图
     * @param topArea        顶部区域
     * @param specifiedArea  指定区域
     * @param variateContext 变量上下文
     * @param os             操作系统
     * @throws IOException ioexception
     */
    public void export(String templatePath,
                       List<MultipartData> map,
                       String topArea,
                       String specifiedArea,
                       String variateContext,
                       OutputStream os
    )throws  IOException {
        try (InputStream is = Files.newInputStream(Paths.get(templatePath))) {
            Transformer transformer = TransformerFactory.createTransformer(is, os);
            XlsArea xlsArea = new XlsArea("Template!"+topArea, transformer);
            XlsArea employeeArea = new XlsArea("Template!"+specifiedArea, transformer);
            EachCommand employeeEachCommand = new EachCommand(variateContext, "items", employeeArea);
            xlsArea.addCommand(specifiedArea, employeeEachCommand);
            Context context = new Context();
            context.putVar("items", map);
            xlsArea.applyAt(new CellRef("Result!A1"), context);
            transformer.write();
        }
    }


    /**
     * 报告前端下载
     *
     * @param response 响应
     * @throws IOException ioexception
     */
    @GetMapping("/jxls")
    public void report(String reportName,HttpServletResponse response) throws IOException {
        OutputStream os=response.getOutputStream();
        //如果想下载试自动填好文件名,需要设置Content-Disposition响应头
        response.setHeader("Content-Disposition", "attachment;filename=" + reportName);
        response.setContentType("application/vnd.ms-excel");

        List<MultipartData> employees = selectSQL.selectResultSet(selectSQL1 -> "SELECT * FROM htpk.report_forms_input_output_one WHERE enterprise_id=57 and year=2022 and month=3");
        export("/Users/xxxx/Me/IDEA/Necrozma/Zacian-888/src/main/java/com/enmalvi/zacian888/controller/JxlsExcel/one.xls",
                employees,"A1:I44","A2:I43","reportFormsInputOutputOne",os);
    }
}

导出工具可以通过jxls-core依赖来实现,但是上面的例子没有用,下面的例子是用了

    public static ResponseEntity<byte[]> downLoadExcel(String sourcePath, Map<String, Object> beanParams)
            throws ParsePropertyException, InvalidFormatException, IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        //读取模板
        InputStream is =ExcelUtil.class.getClassLoader().getResourceAsStream(sourcePath);
        XLSTransformer transformer = new XLSTransformer();
        //向模板中写入内容
        Workbook workbook = transformer.transformXLS(is, beanParams);
        //写入成功后转化为输出流
        workbook.write(os);
        //配置Response信息
        HttpHeaders headers = new HttpHeaders();
        String downloadFileName = UUID.randomUUID().toString() + ".xlsx";
        //防止中文名乱码
        downloadFileName = new String(downloadFileName.getBytes("UTF-8"), "ISO-8859-1");
        headers.setContentDispositionFormData("attachment", downloadFileName);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        //返回
        return new ResponseEntity<byte[]>(os.toByteArray(), headers, HttpStatus.CREATED);
    }
	@RequestMapping("/export")
	//返回ResponseEntity<byte[]>使浏览器下载
	public ResponseEntity<byte[]> exportExcel(HttpServletRequest request, HttpServletResponse response) throws Exception {
		//查询参数
		Map<String, Object> params = new HashMap<String, Object>();
		//结果集
		List<Aip_std> list = stdService.selectAllForExportExcel(params);
		Map<String, Object> beanParams = new HashMap<String, Object>();
		beanParams.put("list", list);
		//下载表格
		return ExcelUtil.downLoadExcel("static/excel/aaa.xlsx",beanParams);
	}

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

1 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 594157浏览

随机文章
SpringBoot—整合Druid(阿里巴巴数据库连接池)
5年前
Kotlin—String的常用方法
4年前
Redis笔记—分布式锁
5年前
SpringBoot—ActiveMQ简单使用
5年前
ElasticSearch—锁和版本控制(九)
5年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 594157 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付