Java填充Word模板

文章目錄

  • 前言
  • 一、設置word模板
    • 普通字段
    • 列表字段
    • 復選框
  • 二、代碼
    • 1. 引入POM
    • 2. 模板放入項目
    • 3.代碼
      • 實體類
      • 工具類
  • 三、測試
  • 四、運行結果
  • 五、注意事項


前言

最近有個Java填充Word模板的需求,包括文本,列表和復選框勾選,寫一個工具類,以此記錄。


提示:以下是本篇文章正文內容,下面案例可供參考

一、設置word模板

選擇文檔中要填充的地方點擊->選擇插入->文檔部件->域->域名(mergeFeild)->填寫變量名稱.

普通字段

在這里插入圖片描述
在這里插入圖片描述
填充完畢:
在這里插入圖片描述

列表字段

操作和普通字段一樣,區別是需要在首行第一列插入列表開始域,首行最后一列插入結束域,中間正常字段。格式為:StartTable:<數組字段名>,EndTable:<數組字段名>
在這里插入圖片描述

復選框

復選框找了好多種方法嘗試沒有成功,最后取巧,和普通字段一樣設置占位符,通過代碼邏輯處理.

在這里插入圖片描述

二、代碼

1. 引入POM

poi/hutool/aspose-words/gson:

	<!-- hutool工具類 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.14</version></dependency><!--word模板數據解析--><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.9.0-beta</version></dependency><!-- word/pdf操作 --><dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>18.8</version></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.9</version></dependency>

2. 模板放入項目

這里是放在項目里,也可以放在云上存儲。
在這里插入圖片描述
破解文件放入resouce,否則會有水印,aspose-words在maven倉庫中也沒有,需要下載后安裝在本地倉庫。

安裝命令

mvn install:install-file -Dfile=路徑/aspose-words-18.8.jar -DgroupId=com.aspose -DartifactId=aspose-words -Dversion=18.8 -Dpackaging=jar

引入破解文件
在這里插入圖片描述

<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>
</License>

jar包和license下載地址

3.代碼

實體類


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Arrays;
import java.util.List;@Data
@AllArgsConstructor
@NoArgsConstructor
public class FillWordDTO implements Serializable {private String name;private String age;private String yuyan;private String yingyu;private String deyu;private String fayu;private String zhengshu;private String yiji;private String erji;private List<ExperienceList> experienceList;public static FillWordDTO create(){FillWordDTO fillWordDTO = new FillWordDTO();fillWordDTO.setName("小王");fillWordDTO.setAge("18");fillWordDTO.setYuyan("?");fillWordDTO.setYingyu("?");fillWordDTO.setDeyu("□");fillWordDTO.setFayu("?");fillWordDTO.setZhengshu("?");fillWordDTO.setYiji("?");fillWordDTO.setErji("□");fillWordDTO.setExperienceList(Arrays.asList(new ExperienceList("小王", "2020-01-01", "2020-01-01", "小王"),new ExperienceList("小王", "2020-01-01", "2020-01-01", "小王")));return fillWordDTO;}
}@Data
@AllArgsConstructor
@NoArgsConstructor
class ExperienceList {private String school;private String startTime;private String endTime;private String remark;
}

工具類


import com.aspose.words.*;
import com.aspose.words.net.System.Data.DataRow;
import com.aspose.words.net.System.Data.DataTable;import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class ContractUtil {private ContractUtil() {}/*** 調整bufferedimage大小* @param source BufferedImage 原始image* @param targetW int  目標寬* @param targetH int  目標高* @param flag boolean 是否同比例調整* @return BufferedImage  返回新image*/public static BufferedImage resizeBufferedImage(BufferedImage source, int targetW, int targetH, boolean flag) {int type = source.getType();BufferedImage target = null;double sx = (double) targetW / source.getWidth();double sy = (double) targetH / source.getHeight();if (flag && sx > sy) {sx = sy;targetW = (int) (sx * source.getWidth());} else if(flag && sx <= sy){sy = sx;targetH = (int) (sy * source.getHeight());}if (type == BufferedImage.TYPE_CUSTOM) { // handmadeColorModel cm = source.getColorModel();WritableRaster raster = cm.createCompatibleWritableRaster(targetW, targetH);boolean alphaPremultiplied = cm.isAlphaPremultiplied();target = new BufferedImage(cm, raster, alphaPremultiplied, null);} else {target = new BufferedImage(targetW, targetH, type);}Graphics2D g = target.createGraphics();g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy));g.dispose();return target;}/*** 填充 word 模板(object數據格式)** @param modelWordByte word模版二進制文件* @param obj     要填充的數據* @return 組合數據之后的word二進制*/public static byte[] fillWordDataByDomain(byte[] modelWordByte, Object obj) {try {Class<?> aClass = obj.getClass();Field[] fields = aClass.getDeclaredFields();Map<String, Object> data = new HashMap<>(fields.length);for (Field field : fields) {PropertyDescriptor pd = new PropertyDescriptor(field.getName(), aClass);Method method = pd.getReadMethod();String key = field.getName();Object value = method.invoke(obj);if (value != null) {data.put(key, value);}}return fillWordDataByMap(modelWordByte, data);} catch (Exception e) {e.printStackTrace();return new byte[0];}}/*** 填充 word 模板(map數據格式)** @param file word二進制* @param data 要填充的數據* @return 組合數據之后的word二進制*/public static byte[] fillWordDataByMap(byte[] file, Map<String, Object> data) throws Exception {byte[] ret = null;if (data == null || data.isEmpty()) {return ret;}try (InputStream is = new ByteArrayInputStream(file);ByteArrayOutputStream out = new ByteArrayOutputStream()) {Document doc = new Document(is);DocumentBuilder builder = new DocumentBuilder(doc);Map<String, String> toData = new HashMap<>();for (Map.Entry<String, Object> entry : data.entrySet()) {String key = entry.getKey();Object value = entry.getValue();// 處理表格數據if (value instanceof List && !key.equals("checkboxOptions")) {DataTable dataTable = fillListData((List) value, key, builder);doc.getMailMerge().executeWithRegions(dataTable);}// 圖片插入else if (value instanceof BufferedImage) {builder.moveToMergeField(key);builder.insertImage((BufferedImage) value);}// 其他普通字段正常填充else {String valueStr = String.valueOf(value);if (value != null && !"null".equals(valueStr)) {toData.put(key, valueStr);}}}// 執行普通字段合并String[] fieldNames = new String[toData.size()];String[] values = new String[toData.size()];int i = 0;for (Map.Entry<String, String> entry : toData.entrySet()) {fieldNames[i] = entry.getKey();values[i] = entry.getValue();i++;}doc.getMailMerge().execute(fieldNames, values);doc.save(out, SaveOptions.createSaveOptions(SaveFormat.DOCX));ret = out.toByteArray();}return ret;}/*** 勾選段落中的復選框字段(適用于 Aspose.Words 18.8)*/private static void checkTheCheckbox(Paragraph paragraph) throws Exception {FieldCollection fields = paragraph.getRange().getFields();int count = fields.getCount();for (int i = 0; i < count; i++) {com.aspose.words.Field field = fields.get(i);if (field.getType() == FieldType.FIELD_FORM_CHECK_BOX) {// 設置字段結果為 "?" 表示勾選(或根據模板實際顯示字符調整)setCheckboxChecked(field, true);}}}private static void setCheckboxChecked(com.aspose.words.Field field, boolean checked) throws Exception {if (checked) {field.setResult("?"); // 根據模板中實際勾選狀態設置} else {field.setResult("□"); // 可選:取消勾選}}/*** 封裝 list 數據到 word 模板中(word表格)** @param list      數據* @param tableName 表格列表變量名稱* @return word表格數據DataTable*/private static DataTable fillListData(List<Object> list, String tableName, DocumentBuilder builder) throws Exception {//創建DataTable,并綁定字段DataTable dataTable = new DataTable(tableName);for (Object obj : list) {//創建DataRow,封裝該行數據DataRow dataRow = dataTable.newRow();Class<?> objClass = obj.getClass();Field[] fields = objClass.getDeclaredFields();for (int i = 0; i < fields.length; i++) {Field field = fields[i];dataTable.getColumns().add(fields[i].getName());PropertyDescriptor pd = new PropertyDescriptor(field.getName(), objClass);Method method = pd.getReadMethod();dataRow.set(i, method.invoke(obj));}dataTable.getRows().add(dataRow);}return dataTable;}//    private static License license = null;/*** 加載 license* 由于 aspose是收費的,若沒有 license,則會出現水印。*/static {try {InputStream is = ContractUtil.class.getResourceAsStream("/License.xml");License license = new License();license.setLicense(is);} catch (Exception e) {throw new RuntimeException("自動加載aspose證書文件失敗!");}}}

三、測試

main方法測試,可以根據實際需求改為response輸出或者上傳到存儲服務器后返回鏈接地址。

    public static void main(String[] args) throws IOException {FillWordDTO fillWordDTO = FillWordDTO.create();// 讀取模板文件byte[] modelByte = Files.readAllBytes(Paths.get("E:\\project\\spring-demo\\src\\main\\resources\\templates\\test.docx"));// 調用工具類,獲取填充數據后的文件byte[] resultByte = ContractUtil.fillWordDataByDomain(modelByte, fillWordDTO);// 處理該二進制文件,此處處理為輸出到桌面File resultFile = new File("C:\\Users\\Lenovo\\Desktop\\demo.docx");FileOutputStream fos = new FileOutputStream(resultFile);fos.write(resultByte);fos.close();}

四、運行結果

在這里插入圖片描述

五、注意事項

在編輯word域代碼時,有時會有隱藏的代碼導致填充失敗

Found end of mail merge region 'experienceList' that does not match start of mail merge region 'jlList'.

出現以上錯誤或者想查看域代碼,按如下操作

在word中依次點擊「文件→選項→高級」,在「顯示文檔內容」區域勾選「顯示域代碼而非域值」,找到報錯域代碼后刪除,重新添加域就解決了。

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

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

相關文章

【MYSQL8】springboot項目,開啟ssl證書安全連接

文章目錄一、開啟ssl證書1、msysql部署時默認開啟ssl證書2、配置文件3、創建用戶并指定ssl二、添加Java信任庫1、使用 keytool 導入證書2、驗證證書是否已導入三、修改連接配置一、開啟ssl證書 1、msysql部署時默認開啟ssl證書 可通過命令查看&#xff1a; SHOW VARIABLES L…

Telegraf vs. Logstash:實時數據處理架構中的關鍵組件對比

在現代數據基礎設施中&#xff0c;Telegraf 和 Logstash 是兩種廣泛使用的開源數據收集與處理工具&#xff0c;但它們在設計目標、應用場景和架構角色上存在顯著差異。本文將從實時數據處理架構、時序數據庫集成、消息代理支持等方面對比兩者的核心功能&#xff0c;并結合實際應…

Vue Vue-route (4)

Vue 漸進式JavaScript 框架 基于Vue2的學習筆記 - Vue-route 編程式導航和幾種路由 目錄 編程式導航 詳情組件 創建組件 設置路由 電影列表 傳參 另一種方式 動態路由 命名路由 別名 總結 編程式導航 點擊電影列表 跳轉電影詳情 詳情組件 創建組件 在views中創…

存在兩個cuda環境,在conda中切換到另一個

進入 openmmlab 環境 conda activate openmmlab 設置環境變量為 CUDA 12.4&#xff08;只影響當前 shell 會話&#xff09; export PATH/usr/local/cuda-12.4/bin:PATHexportLDLIBRARYPATH/usr/local/cuda?12.4/lib64:PATH export LD_LIBRARY_PATH/usr/local/cuda-12.4/lib64:…

Django 視圖(View)

1. 視圖簡介 視圖負責接收 web 請求并返回 web 響應。視圖就是一個 python 函數,被定義在 views.py 中。響應可以是一張網頁的 HTML 內容、一個重定向、一個 404 錯誤等等。響應處理過程如下圖: 用戶在瀏覽器中輸入網址:www.demo.com/1/100Django 獲取網址信息,去除域名和端…

HarmonyOS基礎概念

一、OpenHarmony、HarmonyOS和Harmony NEXT區別OpenHarmony是由開放原子開源基金會&#xff08;OpenAtom Foundation&#xff09;孵化及運營的開源項目&#xff0c;開放原子開源基金會由華為、阿里、騰訊、百度、浪潮、招商銀行、360等十家互聯網企業共同發起組建。目標是面向全…

spark3 streaming 讀kafka寫es

1. 代碼 package data_import import org.apache.spark.sql.{DataFrame, Row, SparkSession, SaveMode} import org.apache.spark.sql.types.{ArrayType, DoubleType, LongType, StringType, StructField, StructType, TimestampType} import org.apache.spark.sql.functions._…

【跟著PMP學習項目管理】每日一練 - 3

1、你是一個建筑項目的項目經理。電工已經開始鋪設路線,此時客戶帶著一個變更請求來找你。他需要增加插座,你認為這會增加相關工作的成本。你要做的第一件事? A、拒絕做出變更,因為這會增加項目的成本并超出預算 B、參考項目管理計劃,查看是否應當處理這個變更 C、查閱…

CentOS 安裝 JDK+ NGINX+ Tomcat + Redis + MySQL搭建項目環境

目錄第一步&#xff1a;安裝JDK 1.8方法 1&#xff1a;安裝 Oracle JDK 1.8方法 2&#xff1a;安裝 OpenJDK 1.8第二步&#xff1a;使用yum安裝NGINX第三步&#xff1a;安裝Tomcat第四步&#xff1a;安裝Redis第五步&#xff1a;安裝MySQL第六步&#xff1a;MySQL版本兼容性問題…

如何設計一個登錄管理系統:單點登錄系統架構設計

關鍵詞&#xff1a;如何設計一個登錄管理系統、登錄系統架構、用戶認證、系統安全設計 &#x1f4cb; 目錄 開篇&#xff1a;為什么登錄系統這么重要&#xff1f;整體架構設計核心功能模塊安全設計要點技術實現細節性能優化策略總結與展望 開篇&#xff1a;為什么登錄系統這么…

論跡不論心

2025年7月11日&#xff0c;16~26℃&#xff0c;陰 緊急不緊急重要 備考ing 備課不重要 遇見&#xff1a;免費人格測試 | 16Personalities&#xff0c;下面是我的結果 INFJ分析與優化建議 User: Anonymous (隱藏) Created: 2025/7/11 23:38 Updated: 2025/7/11 23:43 Exported:…

【面板數據】省級泰爾指數及城鄉收入差距測算(1990-2024年)

對中國各地區1990-2024年的泰爾指數、城鄉收入差距進行測算。本文參考龍海明等&#xff08;2015&#xff09;&#xff0c;程名望、張家平&#xff08;2019&#xff09;的做法&#xff0c;采用泰爾指數測算城鄉收入差距。參考陳斌開、林毅夫&#xff08;2013&#xff09;的做法&…

http get和http post的區別

HTTP GET 和 HTTP POST 是兩種最常用的 HTTP 請求方法&#xff0c;它們在用途、數據傳輸方式、安全性等方面存在顯著差異。以下是它們的主要區別&#xff1a;1. 用途GET&#xff1a;主要用于請求從服務器獲取資源&#xff0c;比如獲取網頁內容、查詢數據庫等。GET 請求不應該用…

I2C集成電路總線

&#xff08;摘要&#xff1a;空閑時&#xff0c;時鐘線數據線都是高電平&#xff0c;主機發送數據前&#xff0c;要在時鐘為高電平時&#xff0c;把數據線從高電平拉低&#xff0c;數據發送采取高位先行&#xff0c;時鐘線低電平時可以修改數據線&#xff0c;時鐘線高電平時要…

為了安全應該使用非root用戶啟動nginx

nginx基線安全&#xff0c;修復步驟。主要是由于使用了root用戶啟動nginx。為了安全應該使用非root用戶啟動nginx一、檢查項和問題檢查項分類檢查項名稱身份鑒別檢查是否配置Nginx賬號鎖定策略。服務配置檢查Nginx進程啟動賬號。服務配置Nginx后端服務指定的Header隱藏狀態服務…

論文解析篇 | YOLOv12:以注意力機制為核心的實時目標檢測算法

前言&#xff1a;Hello大家好&#xff0c;我是小哥談。長期以來&#xff0c;改進YOLO框架的網絡架構一直至關重要&#xff0c;但盡管注意力機制在建模能力方面已被證明具有優越性&#xff0c;相關改進仍主要集中在基于卷積神經網絡&#xff08;CNN&#xff09;的方法上。這是因…

學習C++、QT---20(C++的常用的4種信號與槽、自定義信號與槽的講解)

每日一言相信自己&#xff0c;你比想象中更接近成功&#xff0c;繼續勇往直前吧&#xff01;那么我們開始用這4種方法進行信號與槽的通信第一種信號與槽的綁定方式我們將按鍵右鍵后轉到槽會自動跳轉到這個widget.h文件里面并自動生成了定義&#xff0c;我們要記住我們這個按鈕叫…

Anolis OS 23 架構支持家族新成員:Anolis OS 23.3 版本及 RISC-V 預覽版發布

自 Anolis OS 23 版本發布之始&#xff0c;龍蜥社區就一直致力于探索同源異構的發行版能力&#xff0c;從 Anolis OS 23.1 版本支持龍芯架構同源異構開始&#xff0c;社區就在持續不斷地尋找更多的異構可能性。 RISC-V 作為開放、模塊化、可擴展的指令集架構&#xff0c;正成為…

4萬億英偉達,憑什么?

CUDA正是英偉達所有神話的起點。它不是一個產品&#xff0c;而是一個生態系統。當越多的開發者使用CUDA&#xff0c;就會催生越多的基于CUDA的應用程序和框架&#xff1b;這些殺手級應用又會吸引更多的用戶和開發者投身于CUDA生態。這個正向飛輪一旦轉動起來&#xff0c;其產生…

Unity3D iOS閃退問題解決方案

前言 在Unity3D開發中解決iOS閃退問題需要系統性排查&#xff0c;以下是關鍵步驟和解決方案&#xff1a; 對惹&#xff0c;這里有一個游戲開發交流小組&#xff0c;希望大家可以點擊進來一起交流一下開發經驗呀&#xff01; 1. 獲取崩潰日志&#xff08;關鍵第一步&#xff…