最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中使用模板引擎+Word XML導出復雜Word的步驟

 更新時間:2026年04月15日 09:34:50   作者:一葉龍洲  
這篇文章主要介紹了如何利用Word XML及模板引擎生成復雜Word文檔,先設(shè)計樣式和固定內(nèi)容的Word文檔,保存為XML格式;在需要插入動態(tài)數(shù)據(jù)的地方添加模板占位符;使用Java讀取XML文件,通過模板引擎如FreeMarker或Velocity填充數(shù)據(jù),最終生成新的Word文檔

此方案利用 Word 本身可以保存為 XML 的特性,將設(shè)計好的 Word 文檔另存為 XML(或 .docx 解壓后的 main document 部分),然后在 XML 中嵌入模板標記,最后通過模板引擎生成最終的 Word 文檔。

實現(xiàn)原理

  1. 使用 Microsoft Word 設(shè)計好文檔樣式和固定內(nèi)容,保存為 Word 2003 XML 文檔(.xml)或直接使用 .docx 包內(nèi)的 document.xml。
  2. 在 XML 文件中需要動態(tài)插入數(shù)據(jù)的地方添加模板引擎的占位符(如 ${name}、<#list> 等)。
  3. 在 Java 中讀取該模板文件,使用 FreeMarker 或 Velocity 渲染,將動態(tài)數(shù)據(jù)填充進去。
  4. 將渲染后的 XML 內(nèi)容重新打包成 .docx(如果是操作 .docx 內(nèi)部 XML)或直接保存為 .doc 格式(如果是 Word 2003 XML)。

示例步驟(以 FreeMarker + docx 為例)

  1. 設(shè)計一個 .docx 模板,解壓得到 word/document.xml。
  2. 編輯 document.xml,插入 FreeMarker 語法(注意避免破壞 XML 結(jié)構(gòu),可使用 CDATA 或特殊處理)。
  3. 在 Java 中,讀取 document.xml 作為 FreeMarker 模板,傳入數(shù)據(jù)模型生成填充后的 XML 字符串。
  4. 將該字符串替換回原 .docx 的 document.xml,重新打包為 .docx 文件。
# BeanUtil使用的是Hutool中的工具類 
Map<String, Object> dataMap = BeanUtil.beanToMap(report);

        try {
            // 1. 創(chuàng)建 FreeMarker 配置
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
            // 模板所在目錄
            cfg.setDirectoryForTemplateLoading(new File(templateDir));
            cfg.setDefaultEncoding("UTF-8");
            cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            cfg.setLogTemplateExceptions(false);
            cfg.setWrapUncheckedExceptions(true);
            cfg.setFallbackOnNullLoopVariable(false);

            // 2. 加載模板(已按上述要求修改的 XML 文件)
            Template template = cfg.getTemplate(templatePath);

            // 4. 渲染模板
            try (Writer out = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(templateOutputPath), StandardCharsets.UTF_8))) {
                template.process(dataMap, out);
                response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
                String fileName = report.getUnitName() + "年度自評報告.docx";
                String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replace("+", "%20");
                response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);

# yaml配置
#yearSelfEvaluation:
#  # 模板目錄
#  templateDir: G:\\templates
#  # 模板路徑
#  templatePath: template\\word\\document.xml
#  # 輸出路徑
#  templateOutputPath: G:\\templates\\output2.xml
#  # 模板docx路徑
#  templateDocxPath: G:\\templates\\template.docx

                XmlToDocx.convert(templateDocxPath, templateOutputPath, response);
            }
        } catch (Exception e) {
            log.error("渲染模板失敗", e);
        }



# XmlToDocx

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class XmlToDocx {

    /**
     * 將渲染后的 Word 2003 XML 文件內(nèi)容替換到 docx 模板中,并將生成的 docx 寫入 HttpServletResponse 輸出流
     * @param docxTemplate    原始的 docx 模板路徑(由 XML 另存得來)
     * @param renderedXmlPath 渲染后的 XML 文件路徑
     * @param response        HttpServletResponse 對象,用于輸出 docx
     */
    public static void convert(String docxTemplate, String renderedXmlPath, HttpServletResponse response) throws IOException {
        // 讀取渲染后的 XML 文件內(nèi)容(Java 8 兼容)
        Path xmlPath = Paths.get(renderedXmlPath);
        byte[] xmlBytes = Files.readAllBytes(xmlPath);
        String renderedXml = new String(xmlBytes, StandardCharsets.UTF_8);

        // 創(chuàng)建臨時目錄存放解壓后的文件
        Path tempDir = Files.createTempDirectory("docx");
        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(docxTemplate))) {
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                File outFile = new File(tempDir.toFile(), entry.getName());
                if (entry.isDirectory()) {
                    outFile.mkdirs();
                } else {
                    outFile.getParentFile().mkdirs();
                    try (FileOutputStream fos = new FileOutputStream(outFile)) {
                        byte[] buffer = new byte[8192];
                        int len;
                        while ((len = zis.read(buffer)) != -1) {
                            fos.write(buffer, 0, len);
                        }
                    }
                }
                zis.closeEntry();
            }
        }

        // 替換 document.xml
        Path docXmlPath = tempDir.resolve("word/document.xml");
        Files.write(docXmlPath, renderedXml.getBytes(StandardCharsets.UTF_8));

        // 重新打包為 docx 并直接寫入 response 輸出流
        try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            Files.walk(tempDir).forEach(path -> {
                if (Files.isRegularFile(path)) {
                    String entryName = tempDir.relativize(path).toString().replace('\\', '/');
                    try {
                        zos.putNextEntry(new ZipEntry(entryName));
                        Files.copy(path, zos);
                        zos.closeEntry();
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                }
            });
            zos.finish(); // 確保 ZIP 文件正確結(jié)束
        } finally {
            // 清理臨時目錄
            Files.walk(tempDir)
                    .map(Path::toFile)
                    .forEach(File::delete);
        }
    }

    /**
     * 將渲染后的 Word 2003 XML 文件內(nèi)容替換到 docx 模板中,生成最終的 docx 文件
     * @param docxTemplate    原始的 docx 模板路徑(由 XML 另存得來)
     * @param renderedXmlPath 渲染后的 XML 文件路徑
     * @param outputDocx      輸出 docx 文件路徑
     */
    public static void convert(String docxTemplate, String renderedXmlPath, String outputDocx) throws IOException {
        // 讀取渲染后的 XML 文件內(nèi)容(Java 8 兼容)
        Path xmlPath = Paths.get(renderedXmlPath);
        byte[] xmlBytes = Files.readAllBytes(xmlPath);
        String renderedXml = new String(xmlBytes, StandardCharsets.UTF_8);

        // 創(chuàng)建臨時目錄存放解壓后的文件
        Path tempDir = Files.createTempDirectory("docx");
        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(docxTemplate))) {
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                File outFile = new File(tempDir.toFile(), entry.getName());
                if (entry.isDirectory()) {
                    outFile.mkdirs();
                } else {
                    outFile.getParentFile().mkdirs();
                    try (FileOutputStream fos = new FileOutputStream(outFile)) {
                        byte[] buffer = new byte[8192];
                        int len;
                        while ((len = zis.read(buffer)) != -1) {
                            fos.write(buffer, 0, len);
                        }
                    }
                }
                zis.closeEntry();
            }
        }

        // 替換 document.xml
        Path docXmlPath = tempDir.resolve("word/document.xml");
        Files.write(docXmlPath, renderedXml.getBytes(StandardCharsets.UTF_8));

        // 重新打包為 docx
        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputDocx))) {
            Files.walk(tempDir).forEach(path -> {
                if (Files.isRegularFile(path)) {
                    String entryName = tempDir.relativize(path).toString().replace('\\', '/');
                    try {
                        zos.putNextEntry(new ZipEntry(entryName));
                        Files.copy(path, zos);
                        zos.closeEntry();
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                }
            });
        } finally {
            // 清理臨時目錄
            Files.walk(tempDir)
                 .map(Path::toFile)
                 .forEach(File::delete);
        }
    }

    public static void main(String[] args) throws IOException {
        convert("G:\\templates\\template.docx", "G:\\templates\\output2.xml", "F:\\test2.docx");
        System.out.println("docx 文件已生成, haha ");
    }
}

到此這篇關(guān)于Java中使用模板引擎+Word XML導出復雜Word的步驟的文章就介紹到這了,更多相關(guān)Java導出復雜Word內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java?面向?qū)ο笸ㄟ^new揭開對象實例化

    Java?面向?qū)ο笸ㄟ^new揭開對象實例化

    各位鐵汁們大家好呀,我們上次博客講了,通過?Student?student1?=?new?Student();就可以實例化一個對象,這個對象就有Student類中的所以成員變量??墒?對象student1?和?類Student到底是怎樣建立聯(lián)系的,在內(nèi)存中到底發(fā)生了什么
    2022-04-04
  • Java中try catch 的基本用法示例

    Java中try catch 的基本用法示例

    這篇文章主要給大家介紹了關(guān)于Java中try catch 的基本用法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • JAVA使用quartz添加定時任務(wù),并依賴注入對象操作

    JAVA使用quartz添加定時任務(wù),并依賴注入對象操作

    這篇文章主要介紹了JAVA使用quartz添加定時任務(wù),并依賴注入對象操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • JDK1.7中HashMap的死循環(huán)問題及解決方案

    JDK1.7中HashMap的死循環(huán)問題及解決方案

    這篇文章主要為大家介紹了JDK1.7中HashMap的死循環(huán)問題及解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • IDEA EasyCode 一鍵幫你生成所需代碼

    IDEA EasyCode 一鍵幫你生成所需代碼

    這篇文章主要介紹了IDEA EasyCode 一鍵幫你生成所需代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • Java19新特性中結(jié)構(gòu)化并發(fā)的使用

    Java19新特性中結(jié)構(gòu)化并發(fā)的使用

    Java19在并發(fā)編程領(lǐng)域引入了一個全新的概念:結(jié)構(gòu)化并發(fā),這一特性旨在簡化并發(fā)任務(wù)的管理,提升多線程程序的可維護性和安全性,使其生命周期和控制流更加有序和明確,感興趣的可以了解一下
    2024-09-09
  • Maven Plugins報錯的正確解決方案

    Maven Plugins報錯的正確解決方案

    文章介紹了如何解決Maven插件報錯的問題,建議在本地Maven倉庫目錄中刪除報錯的插件文件,然后在IDEA中刷新Maven以重新加載,同時,作者還提到了一個無效的方法,即通過修改Maven的setting.xml文件添加鏡像源
    2026-03-03
  • Elasticsearch中store field與non-store field的區(qū)別說明

    Elasticsearch中store field與non-store field的區(qū)別說明

    這篇文章主要介紹了Elasticsearch中store field與non-store field的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • RocketMQ?消息Message的結(jié)構(gòu)和使用方式詳解

    RocketMQ?消息Message的結(jié)構(gòu)和使用方式詳解

    Message是RocketMQ的數(shù)據(jù)包,它不僅是業(yè)務(wù)數(shù)據(jù)的載體,更是路由、過濾、追蹤、延遲、事務(wù)等功能的基礎(chǔ),掌握Message,你就掌握了RocketMQ的語言,本文給大家介紹什么是?Message及理解Message的結(jié)構(gòu)、屬性、生命周期和使用方式,感興趣的朋友一起看看吧
    2025-08-08
  • 淺談SpringBoot自動配置的坑

    淺談SpringBoot自動配置的坑

    SpringBoot的自動配置簡化了開發(fā),但也會帶來一些問題,本文通過幾個實際案例探討了一些自動配置可能帶來的問題,下面就來詳細的介紹下
    2026-05-05

最新評論

天峨县| 奉贤区| 响水县| 阳原县| 鹤峰县| 雷波县| 鲁甸县| 榆中县| 民丰县| 南投市| 苏尼特右旗| 清涧县| 吉木乃县| 贵溪市| 樟树市| 桐梓县| 区。| 揭阳市| 宜良县| 水城县| 内黄县| 汤原县| 清河县| 绥阳县| 河南省| 东阳市| 房山区| 昭苏县| 旺苍县| 江陵县| 馆陶县| 陇川县| 孙吴县| 丹棱县| 普兰店市| 金秀| 绿春县| 凌海市| 武汉市| 五家渠市| 滨州市|