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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82104.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82104.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82104.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CodeBuddy實現pdf批量加密

本文所使用的 CodeBuddy 免費下載鏈接&#xff1a;騰訊云代碼助手 CodeBuddy - AI 時代的智能編程伙伴 前言 在信息爆炸的時代&#xff0c;PDF 格式因其跨平臺性和格式穩定性&#xff0c;成為辦公、學術、商業等領域傳遞信息的重要載體。從機密合同到個人隱私文檔&#xff0c…

如何在PyCharm2025中設置conda的多個Python版本

前言 體驗的最新版本的PyCharm(Community)2025.1.1&#xff0c;發現和以前的版本有所不同。特別是使用Anaconda中的多個版本的Python的時候。 關于基于Anaconda中多個Python版本的使用&#xff0c;以及對應的Pycharm&#xff08;2023版&#xff09;的使用&#xff0c;可以參考…

STM32F103 HAL多實例通用USART驅動 - 高效DMA+RingBuffer方案,量產級工程模板

導言 《STM32F103_LL庫寄存器學習筆記12.2 - 串口DMA高效收發實戰2&#xff1a;進一步提高串口接收的效率》前陣子完成的LL庫與寄存器版本的代碼&#xff0c;有一個明顯的缺點是不支持多實例化。最近&#xff0c;計劃基于HAL庫系統地梳理一遍bootloader程序開發。在bootloader程…

【數據結構】棧和隊列(上)

目錄 一、棧&#xff08;先進后出、后進先出的線性表&#xff09; 1、棧的概念及結構 2、棧的底層結構分析 二、代碼實現 1、定義一個棧 2、棧的初始化 3、入棧 3、增容 4、出棧 5、取棧頂 6、銷毀棧 一、棧&#xff08;先進后出、后進先出的線性表&#xff09; 1、…

Vue 3 官方 Hooks 的用法與實現原理

Vue 3 引入了 Composition API&#xff0c;使得生命周期鉤子&#xff08;hooks&#xff09;在函數式風格中更清晰地表達。本篇文章將從官方 hooks 的使用、實現原理以及自定義 hooks 的結構化思路出發&#xff0c;全面理解 Vue 3 的 hooks 系統。 &#x1f4d8; 1. Vue 3 官方生…

大語言模型 17 - MCP Model Context Protocol 介紹對比分析 基本環境配置

MCP 基本介紹 官方地址&#xff1a; https://modelcontextprotocol.io/introduction “MCP 是一種開放協議&#xff0c;旨在標準化應用程序向大型語言模型&#xff08;LLM&#xff09;提供上下文的方式。可以把 MCP 想象成 AI 應用程序的 USB-C 接口。就像 USB-C 提供了一種…

云原生安全之PaaS:從基礎到實踐的技術指南

??「炎碼工坊」技術彈藥已裝填! 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 云原生安全之PaaS:從基礎到實踐的技術指南 一、基礎概念 PaaS(Platform as a Service)平臺 PaaS是一種云計算服務模型,為開發者提供應用程序的開發、部署和運行環境,涵…

Chrome中http被強轉成https問題

原因&#xff1a;2023年11月1日&#xff0c;chrome發布HTTPS-Upgrades功能&#xff0c;在用戶訪問 http:// 的舊鏈接之后&#xff0c;會自動嘗試跳轉到通過加密的 https:// 協議&#xff0c;訪問該網站。且探測到 https 服務存在也會自動改成 https。 親測兩種方案可行&#x…

Linux 操作文本文件列數據的常用命令

文章目錄 Linux 操作文本文件列數據的常用命令基本列處理命令高級列處理列數據轉換和排序列數據統計和分析 Linux 操作文本文件列數據的常用命令 Linux 提供了多種強大的命令來處理文本文件中的列數據&#xff0c;以下是一些最常用的命令和工具&#xff1a; 基本列處理命令 c…

如何理解線性判別分析(LDA)算法?

在高維數據空間中,特征變量呈指數級增長,信息分布密集且復雜。研究者在面對海量特征時,仿佛置身于一幅結構高度抽象且維度交織的多變量圖景之中,其解析與建模猶如在一幅復雜的數據宇宙圖譜中導航,既需理論框架的指引,也依賴于算法工具的精確刻畫。如何從眾多維度中篩選出…

鴻蒙UI開發——Builder函數的封裝

1、問題引入 我們在開發中可能會遇到這樣一個問題&#xff1a;將一個Builder修飾后的函數用變量或者數組記錄下來&#xff0c;在業務其他地方使用這些Builder函數。 舉個例子&#xff0c;有下面一段代碼&#xff1a; Builderfunction builderElement() {}let builderArr: Fu…

ARM筆記-ARM指令集

第三章 ARM指令集 3.1 ARM指令集簡介 ARM微處理器的ARM指令集 &#xff0c;所有的指令長度都是32位 &#xff0c;并且大多數指令都在一個單獨指令周期內執行。 主要特點&#xff1a; 指令是條件執行的ARM微處理器的指令集是加載/存儲型的在多寄存器操作指令中一次最多可以完成…

Spring Boot接口通用返回值設計與實現最佳實踐

一、核心返回值模型設計&#xff08;增強版&#xff09; package com.chat.common;import com.chat.util.I18nUtil; import com.chat.util.TraceUtil; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter;import java.io.Serializable;/*** 功能: 通…

2025年上半年軟件架構師考試回憶版【持續更新】

文章目錄 案例分析1、端AI相對于云AI的優勢2、redis持久化&#xff0c;主從庫3、解釋器架構風格4、知識圖譜5、區塊鏈 論文1、基于事件驅動的模型2、多模型數據庫及其應用3、負載均衡設計方法4、論軟件測試理論及其應用 考試感受 2025年軟件考試架構考試于5月24日如期舉行&…

Windows下編譯Zipios

本文記錄在Windows下編譯Zipios的流程。 注1&#xff1a;文章內容會不定期更新。 零、環境 操作系統Windows 11VS Code1.92.1Git2.34.1Visual StudioVisual Studio Community 2022CMake3.22.1 一、安裝依賴 二、編譯 2.1 下載代碼 git clone https://github.com/Zipios/Zi…

SOC-ESP32S3部分:11-任務創建

飛書文檔https://x509p6c8to.feishu.cn/wiki/EH3owsPahisvl6kL6k3cqaQ3n0g 在我們學習單片機的時候&#xff0c;main函數入口中一般有一個while大循環在不停輪詢&#xff0c;如果我們需要實現多種不同的業務&#xff0c;就需要用到狀態機&#xff0c;根據不同時刻的要求執行不…

[Git] 如何進行版本回退

版本控制系統最重要的能力之一&#xff0c;就是能夠輕松地在項目的不同歷史版本之間切換。有時&#xff0c;你可能發現最近的修改引入了嚴重問題&#xff0c;或者需要回到之前的某個節點重新開始。這時&#xff0c;“版本回退”功能就派上用場了。 版本回退&#xff1a;反方向…

易貝平臺關鍵字搜索技術深度解析

一、核心搜索機制 關鍵詞匹配原理 采用TF-IDF算法計算關鍵詞權重 支持同義詞擴展&#xff08;如"phone"匹配"cellphone"&#xff09; 標題權重 > 副標題 > 商品描述 搜索排序因素 # 搜索權重模擬計算 def calculate_rank(keyword, item): title…

深度剖析 MCP SDK 最新版:Streamable HTTP 模式

好記憶不如爛筆頭&#xff0c;能記下點東西&#xff0c;就記下點&#xff0c;有時間拿出來看看&#xff0c;也會發覺不一樣的感受. 目錄 一、概述 二、快速上手&#xff1a;開啟 Streamable HTTP 服務端開啟 客戶端連接 三、深入兩個核心參數 stateless_http json_resp…

樹莓派開箱上手教程(無需顯示器版)

樹莓派開箱上手教程&#xff08;無需顯示器版&#xff09; 硬件準備 名稱參數電源適配器5V電源適配器&#xff0c;至少需要3A的額定電流&#xff0c;配備USB Type-C輸出接頭microSD卡用來將樹莓派的操作系統安裝到上邊&#xff0c;至少需要8GB容量&#xff0c;一般建議16GB及以…