springboot使用xdoc-report包导出word
背景:项目需要使用xdoc-report.jar根据设置好的word模版,自动填入数据 导出word
框架使用
我的需求是我做一个模板然后往里面填充内容就导出我想要的word文件,问了下chatgpt还有百度,最后选用了xdocreport这个框架,主要它使用docx模板以及freemarker模板引擎就可以做导出,不用各种转换,而且文档demo也很齐全,虽然最新的版本已经两年没更新了!!参考文档
制作模板
模板效果图如下
文字显示
比如说,我们需要显示一些文档的元数据,例如 文档的标题,文档的时间等等可以单独显示的属性。我们可以使用一个对象来封装这些属性,现在我封装了一个Project对象
class ExportProject() { var title: String? = null var type: String? = null var car: String? = null var className: String? = null var date: String? = null var problem: String? = null var range: String? = null var signature: IImageProvider? = null constructor(vo: DriverCheckVo) : this() { type = vo.spec car = vo.usageCode className = vo.shift date = "${vo.date?.year} 年 ${vo.date?.monthValue} 月 ${vo.date?.dayOfMonth} 日" xproblem = vo.problemSummary }
}
生成word文档时,只要new一个对象实例,扔到xdocreport的context中,就可以使用啦。当然具体展示还是需要在docx模板上做特殊处理的。比如我想控制我生成的docx文件中的标题,我直接在标题处,生成一个域即可,具体操作步骤如下,(下列例子中使用了wps),另外模版的文件格式名一定是docx
step-》 将光标放在标题处-》点击插入 -》选择文档部件-》点击域-》选择邮件合并-》输入变量 ${project.title} -》 点击确定
效果如下!
其他的地方,例如年份,也是一样 输入 变量 ${project.range}即可。这样我们就可以将需要单独显示的文字,控制在xdocreport context里面 project变量里面了,当然具体怎么取名,放在哪,都随便。
列表嵌套
上面说了一些单独显示的文字显示,那列表控制如何显示呢,比如下图
首先还是定义控制对象
class DriverCheckData {
/**
* 大检查项
*/
var name: String? = null /**
* 是否有小检查项
*/
var hasLittleCheck: Boolean? = false /**
* 小检查项列表
*/
var items: List<DriverCheckItem>? = null
} class DriverCheckItem {
/**
* 小检查项列表
*/
var name: String? = null /**
* 序号
*/
var sequence: Int? = null /**
* 检查内容
*/
var content: String? = null /**
* 检查结果
*/
var result: String? = null
}
定义了两个类,大检查项类以及小检查项类,大检查项内嵌小检查项。这样我们在输出列表数据时,只需要定义一个大检查项列表就可以展示了,因为是展示的还是文本,所以定义还是跟上面一样,使用域来关联变量,写法么,就跟mybatis,el表达式差不多。
即使用 [#list noTag as data] 这个将 notag 这个list 里面的子元素 定义变量名为 data,[/#list] 代表列表结束,就跟html的标签对一样,然后是,data的数据展示了,直接使用 ${data.name} 这样的域就可以展示data里面的属性,可以看到,我这个图里面 用的不是[#list noTag as data] ,而是加了前缀,list的结尾也加了后缀,这是为了处理docx里面的表格而做的处理。照猫画虎即可,如果不是在表格里展示,直接使用
以下案例即可
«[#list developers as developer]»Name: «${developer.name}»Mail : [${developer.mail}]Mail2 : [${developer.mail}]«[/#list]»
图片展示
例如我们展示文本,使用了域,对于图片呢,需要使用书签,如果我说的不清楚,直接看官方demo
流程如下:
step-》添加一个图片(啥图片都行)当做模板,选中图片-》点击插入-》书签-》添加书签名-》添加
这个书签名非常重要,他的名字对应了我们在context里面设置的变量。如果是单独展示,直接设置一级变量名,与书签名对应即可,但是如果是图片列表,而且我们放在对象里面,怎么办呢?使用官方示例,不用[#list noTag as data],而是在java/kotlin程序里面对该列表进行处理,使用列表名中对应的图片属性即可。详见 官方示例
代码展示
springboot加载模板实例
@Component
@Data
class ExportInstanceConfig(
private val resourceLoader: ResourceLoader
) {
private var driverExport: IXDocReport? = null
private var checkExport: IXDocReport? = null
@PostConstruct
fun init(){
driverExport = getInstance("classpath:check.docx")
checkExport = getInstance("classpath:driver.docx")
} private fun getInstance(path:String):IXDocReport{
var inputStream :InputStream? = null
try{
val res = resourceLoader.getResource(path)
inputStream = res.inputStream
return XDocReportRegistry
.getRegistry()
.loadReport(
inputStream,
TemplateEngineKind.Freemarker
)
}catch (e:IOException){
throw e
}finally {
inputStream?.close()
}
}
将导出的数据转成二进制数组
private fun exportProcess(report: IXDocReport, context: IContext): ByteArray { val bos = ByteArrayOutputStream() val res: ByteArray try { // 导入模板 report.process(context, bos) res = bos.toByteArray() } catch (e: IOException) { throw e } finally { bos.close() } return res
}
进行单独图片导出
private fun exportDriverCheckDocx(param: DriverCheckVo, title: String): ByteArray { val report = exportInstanceConfig.getDriverExport() val metadata = report.createFieldsMetadata() val context = report.createContext() val exportProject = ExportProject(param).apply { this.title = title } // 这里是对上传的图片的base64编码 进行解码val image = param.checkPeopleDocumentary?.split(",")?.let { decoder.decode(it[1]) } if (image != null) { // 对应模板中,单独显示的图片metadata.addFieldAsImage("signature") // 导出图片时,图片对应的类 的格式context.put("signature",ByteArrayImageProvider(ByteArrayInputStream(image))) } val noTags = param.dataList?.filter { it.hasLittleCheck == false }?.toList() val hasTags = param.dataList?.filter { it.hasLittleCheck == true }?.toList() context.put("project", exportProject) context.put("noTag", noTags) context.put("hasTag", hasTags) return exportProcess(report, context)
}
图片迭代导出
private fun exportCheckDocx(param: CheckVo, title: String, range: String?): ByteArray { val report = exportInstanceConfig.getCheckExport() val context = report.createContext() val metadata = report.createFieldsMetadata() // 对带图片列表的对象进行load处理,方便模板识别metadata.load("re",CheckRecord::class.java,true) val exportProject = ExportProject().apply { this.title = title this.range = range } val noTags = param.dataList?.filter { it.hasLittleCheck == false }?.toList() param.recordList?.forEach { it -> if(it.documentary!=null){ val image = it.documentary?.split(",")?.let { decoder.decode(it[1]) } if (image != null) { it.signature = ByteArrayImageProvider(image).apply { this.setSize(100f,100f) } } } } val hasTags = param.dataList?.filter { it.hasLittleCheck == true }?.toList() context.put("project", exportProject) context.put("noTag", noTags) context.put("hasTag", hasTags) // 将带图片的列表加载进上下文context.put("re", param.recordList) return exportProcess(report, context)
}
带图片的迭代对象定义
class CheckRecord {
/**
* 设备型号
*/
var spec: String? = null /**
* 车号
*/
var usageCode: String? = null /**
* 检查日期
*/
var checkDate: String? = null /**
* 检查情况
*/
var checkContent: String? = null /**
* 整改要求及完成日期
*/
var require: String? = null /**
* 检查人签名
*/
var documentary: String? = null
/**
* 图片实体
*/
@get:FieldMetadata(images = [ ImageMetadata(name = "signature", behaviour = NullImageBehaviour.RemoveImageTemplate) ])
var signature: IImageProvider? = null
}
文件压缩
因为导出docx文件有多个,要求压缩成一个压缩包,这边使用的是
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.23.0</version>
</dependency>
代码如下,将得到的docx二进制数组转成zip
// 不同类型的文件对应不同的MIME类型
response.apply { characterEncoding = "UTF-8" // 设置编码字符 setHeader("Content-disposition", "attachment;filename=${URLEncoder.encode("下载文件" + ".zip", "utf-8")}") contentType = "application/zip"
}
val zipOutputStream = ZipArchiveOutputStream(response.outputStream)
try { var sequence = 0 taskExportVo.forEach { // 实例化 ZipEntry 对象,源文件数组中的当前文件 sequence++ val date = it.date val fileName = when (query.inspType) { EqpInspPlanType.日常检查 -> "${it.eqpCategoryName}${it.exportType}${date?.year}${date?.monthValue}${date?.dayOfMonth}-$sequence.docx" EqpInspPlanType.点检员点检 -> "${it.eqpCategoryName}${it.exportType}.docx" EqpInspPlanType.专检组专检 -> "${it.eqpCategoryName}${it.exportType}.docx" } zipOutputStream.putArchiveEntry(ZipArchiveEntry(fileName)) // 导出单个docx文件val data = memEqpInspCheckRecordService.exportDocx(it) // 写入zip流data?.let { it1 -> zipOutputStream.write(it1, 0, it1.size) } zipOutputStream.closeArchiveEntry() }
} catch (i: IOException) { i.printStackTrace()
} finally{zipOutputStream.close()
}
其他:可视化Word模板设计和导出可以使用NopReport引擎,它不依赖于poi库,直接使用Word进行模板设计。编辑juejin.cn
附录:Java版本读取数据库数据生成word模板并压缩成Zip中返回
参考:https://juejin.cn/post/7265673876032766015
相关文章:

springboot使用xdoc-report包导出word
背景:项目需要使用xdoc-report.jar根据设置好的word模版,自动填入数据 导出word 框架使用 我的需求是我做一个模板然后往里面填充内容就导出我想要的word文件,问了下chatgpt还有百度,最后选用了xdocreport这个框架,主…...
leetcode 3356. 零数组变换 II 中等
给定一个长度为 n 的整数数组 nums 和一个二维数组 queries,其中 queries[i] [li, ri]。 对于每个查询 queries[i]: 在 nums 的下标范围 [li, ri] 内选择一个下标 子集。将选中的每个下标对应的元素值减 1。 零数组 是指所有元素都等于 0 的数组。 …...

重拾GMP
目录 GMP总结 线程协程三家对比GMP调度模型 mgp过一遍流程 g 一个G的生命周期 mpschedt全局队列g0视角看看G的调度流程 四大调度类型 主动调度被动调度正常调度抢占调度 宏观的调度流程上面流程的具体细节 schedule()findRunnable()execute()gosched_m()park_m()与ready()goe…...

实验分享|基于千眼狼sCMOS科学相机的流式细胞仪细胞核成像实验
实验背景 流式细胞仪与微流控技术,为细胞及细胞核成像提供新的路径。传统流式细胞仪在细胞核成像检测方面存在检测通量低,荧光信号微弱等局限,故某光学重点实验室开发一种基于高灵敏度sCMOS科学相机并集成在自组荧光显微镜的微流控细胞核成像…...
C++学习:六个月从基础到就业——C++11/14:其他语言特性
C学习:六个月从基础到就业——C11/14:其他语言特性 本文是我C学习之旅系列的第四十四篇技术文章,也是第三阶段"现代C特性"的第六篇,主要介绍C11/14中引入的其他重要语言特性。查看完整系列目录了解更多内容。 引言 在前…...

【Linux笔记】——线程池项目与线程安全单例模式
🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:Linux 🌹往期回顾🌹: 【Linux笔记】——简单实习一个日志项目 🔖流水不争,争的是滔滔不息 一、线程池设计二…...
数据驱动的社会舆情监测与分析——用算法洞察世界脉动
数据驱动的社会舆情监测与分析——用算法洞察世界脉动 在信息爆炸的时代,社会舆情的变化可以在极短时间内产生深远影响。从企业品牌到公共政策,社交媒体和新闻平台上的讨论能够直接影响决策者的策略制定。因此,数据驱动的舆情监测与分析 逐渐成为政府、企业以及社会机构的重…...
OD 算法题 B卷 【最佳植树距离】
文章目录 最佳植树距离 最佳植树距离 在直线的公路上种树,给定坑位数量和位置,及需要种多少棵树苗;树苗之间的最小距离是多少时,可以保证种的最均匀(树苗之间的最小距离最大); 输入描述&#…...

ZooKeeper 原理解析及优劣比较
大家好,这里是架构资源栈!点击上方关注,添加“星标”,一起学习大厂前沿架构! 引言 在分布式系统中,服务注册、配置管理、分布式锁、选举等场景都需要一个高可用、一致性强的协调服务。Apache ZooKeeper 凭…...
实战5:个性化数字艺术生成与销售
盈利思路 数字艺术销售: 平台销售:将生成的数字艺术作品上传到像OpenSea、Foundation等NFT平台进行售卖。每一件独特的艺术品可以通过NFT技术保证其唯一性,吸引收藏家和投资者。 定价策略:根据作品的复杂度、创意性以及市场需求来…...

是德科技 | 单通道448G未来之路:PAM4? PAM6? PAM8?
内容来源:是德科技 随着数据中心规模的不断扩大以及AI大模型等技术的兴起,市场对高速、大容量数据传输的需求日益增长。例如,AI训练集群中GPU等设备之间的互联需要更高的传输速率来提升效率。在技术升级方面,SerDes技术的不断进步…...

OceanBase 开发者大会,拥抱 Data*AI 战略,构建 AI 数据底座
5 月 17 号以“当 SQL 遇见 AI”为主题的 OceanBase 开发者大会在广州举行,因为行程的原因未能现场参会,仍然通过视频直播观看了全部的演讲。总体来说,这届大会既有对未来数据库演进方向的展望,也有 OceanBase 新产品的发布&#…...

STM32IIC协议基础及Cube配置
STM32IIC协议基础及Cube配置 一,IC协议简介1,核心特点2,应用场景 二,IC协议基础概念1,总线结构2,主从架构3,设备寻址4,起始和停止条件5,数据传输6,应答机制 三…...

CNN vs ViT:图像世界的范式演进
一、图像建模,是不是也可以“大一统” 在前文中我们提到,多模态大模型打破“只能处理文字”的限制。 在 NLP 世界里,Transformer 已经证明自己是理解语言的王者。那么在图像世界,我们是否也能有一种“通用架构”,让模…...

cocos creator使用jenkins打包微信小游戏,自动上传资源到cdn,windows版运行jenkins
cocos 版本2.4.11 在windows上jenkins的具体配置和部署,可参考上一篇文章cocos creator使用jenkins打包流程,打包webmobile_jenkins打包,发布,部署cocoscreator-CSDN博客 特别注意,windows上运行jenkins需要关闭windows自己的jenkins服务&a…...

定时器的两种实现方式
1、基于优先级队列/堆 队列是先进先出,优先级队列是优先级越高就存放在队列之前,我们可以将过期时间越早设置为优先级越高,那么临近过期时间的任务就会在队列前面,距离过期时间越晚的任务就在队列后面。 可以分配一个线程&#…...
Python、Pytorch、TensorFlow、Anconda、PySide、Jupyter
一、Python Python是一种高级、通用、解释型的开源编程语言,由Guido van Rossum于1990年代初设计。它具有以下显著特点: 1.语言特性 (1) 语法简洁易读,接近自然语言(如print(“Hello World!”)) (2) 采用强制缩进而非大括号定义代码块 (3) 支持面向对象、函数式和过…...

[Java实战]Spring Boot整合MinIO:分布式文件存储与管理实战(三十)
[Java实战]Spring Boot整合MinIO:分布式文件存储与管理实战(三十) 一、MinIO简介与核心原理 MinIO 是一款高性能、开源的分布式对象存储系统,兼容 Amazon S3 API,适用于存储图片、视频、日志等非结构化数据。其核心特…...
MacBook Air A2179(Intel版)安装macOS Catalina所需时间
MacBook Air A2179(Intel版)安装macOS Catalina所需时间如下: 一、标准安装时间范围 常规安装(通过App Store) • 下载时间:约30-60分钟(取决于网络速度,安装包约8GB) •…...

AI在人力资源领域的应用:把握时代浪潮
借鉴历史经验,引领技术变革 历史总是呈现出惊人的相似性。十年前,众多企业未能及时洞察移动技术与社交技术的潜在价值,迟迟没有将这些创新引入职场环境。随着时间推移,这些组织才意识到BYOD(自带设备办公)…...
【VxWorks 实时操作系统(RTOS)】常用函数汇总
VxWorks 实时操作系统(RTOS)中的核心函数 1. taskSpawn 函数 功能:用于动态创建并激活一个新任务(线程)。参数解析(以 VxWorks 为例):int taskSpawn(char *name, // 任务名…...

vr制作公司提供什么服务?
随着科技的迅猛进步,虚拟现实(Virtual Reality,简称VR)技术已经悄然渗透到我们的日常生活与工作中,成为推动数字化转型的重要力量。VR制作公司,作为前沿领域的探索者和实践者,以专业的技术和创新…...

下一代电子电气架构(EEA)的关键技术
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...

matlab慕课学习3.5
于20250520 3.5 用while 语句实现循环结构 3.5.1while语句 多用于循环次数不确定的情况,循环次数确定的时候用for更为方便。 3.5.2break语句和continue语句 break用来跳出循环体,结束整个循环。 continue用来结束本次循环,接着执行下一次…...
大语言模型(LLM)如何通过“思考时间”(即推理时的计算资源)提升推理能力
大语言模型(LLM)如何通过“思考时间”(即推理时的计算资源)提升推理能力 核心围绕人类思维机制、模型架构改进、训练方法优化等展开 一、人类思维的启发:快思考与慢思考 类比心理学: 人类思维分两种模式: 快思考(系统1):直觉驱动,快速但易出错(如估算简单问题)。…...
Ollama 如何在显存资源有限的情况下合理分配给不同的服务?
在 Windows PowerShell 中启动两个 Ollama 实例的推荐步骤是: 打开第一个 PowerShell 窗口,并执行: $env:OLLAMA_HOST"0.0.0.0:11434" ollama serve打开第二个 PowerShell 窗口,并执行: $env:OLLAMA_HOST&qu…...

Qt音视频开发过程中一个疑难杂症的解决方法/ffmpeg中采集本地音频设备无法触发超时回调
一、前言 最近在做实时音视频通话的项目中,遇到一个神奇的问题,那就是用ffmpeg采集本地音频设备,当音频设备拔掉后,采集过程会卡死在av_read_frame函数中,尽管设置了超时时间,也设置了超时回调interrupt_c…...
基于注意力机制与iRMB模块的YOLOv11改进模型—高效轻量目标检测新范式
随着深度学习技术的发展,目标检测在自动驾驶、智能监控、工业质检等场景中得到了广泛应用。针对当前主流目标检测模型在边缘设备部署中所面临的计算资源受限和推理效率瓶颈问题,YOLO系列作为单阶段目标检测框架的代表,凭借其高精度与高速度的平衡优势,在工业界具有极高的应…...

PEFT库PromptTuningConfig 配置
PEFT库 PromptTuningConfig 配置 "Prompt Tuning"的参数高效微调 PromptTuningConfig 核心参数解析 1. task_type="CAUSAL_LM" 作用:指定任务类型为因果语言模型(Causal LM)。说明:因果语言模型从左到右生成文本(如GPT系列),这与任务需求匹配(模…...

操作系统----软考中级软件工程师(自用学习笔记)
目录 1、计算机系统层次结构 2、程序顺序执行的特征 3、程序并发执行的特征 4、三态模型 5、同步与互斥 6、信号量机制 7、PV操作 8、死锁 9、进程资源图 10、死锁避免 11、线程 12、程序局部性原理 13、分页存储管理 14、单缓冲器 15、双缓冲区 16、磁盘调度算…...