Java Word文档处理,基于docx4j模板标签实现docx文本替换

基于docx4j11.4.9,实现word中打${aaa}的内容替换

=====================================================================================

import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.finders.RangeFinder;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.org.apache.poi.util.IOUtils;
import org.docx4j.wml.*;
import org.jvnet.jaxb2_commons.ppp.Child;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipOutputStream;



/**
 * 关于文件操作的工具类
 *
 * @author kaizen
 * @date 2018-10-23 17:21:36
 */
public class Docx4jUtils {

    private static final Logger logger = LoggerFactory.getLogger(Docx4jUtils.class);

    /**
     * 替换变量并输出word文档
     * @param map
     * @param outputStream
     */
    public static  void replaceDocUseDoc4j(InputStream inputStream,Map<String, String> map, OutputStream outputStream) throws Docx4JException, IOException {
        WordprocessingMLPackage doc = null;
        MainDocumentPart mainDocumentPart = null;
        try (InputStream is = inputStream) { // 包装为自动关闭的流
            doc = WordprocessingMLPackage.load(is);
        } //
        try {
            if (null != map && !map.isEmpty()) {
                // 将${}里的内容结构层次替换为一层  暂时去掉此处调用
                mainDocumentPart = doc.getMainDocumentPart();
                cleanDocumentPart(mainDocumentPart,map);
                // 替换文本内容
                mainDocumentPart.variableReplace(map);
            }
            // 输出word文件
            doc.save(outputStream);
            outputStream.flush();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }finally {
            // 强制清理docx4j对象引用,关键的内存释放步骤
            releaseDocxResources(doc, mainDocumentPart);

            // 主动建议GC(仅为辅助,核心是上面的引用清理)
            System.gc();
            System.runFinalization();
        }
    }


/**
 * 从Word文档中提取文本占位符
 * @param inputStream 输入流,包含Word文档内容
 * @return 返回包含所有占位符的Set集合
 * @throws Exception 如果处理文档过程中出现错误
 */
    public static  Set<String> extractWordPlaceholders(InputStream inputStream) throws Exception {
        // 加载WordprocessingMLPackage文档对象
        WordprocessingMLPackage doc = null;
        Set<String> placeholders = new HashSet<>();
        try (InputStream is = inputStream) { // 包装为自动关闭的流,确保资源被正确释放
            doc = WordprocessingMLPackage.load(is); // 从输入流加载Word文档

            // 获取文档的主部分
            MainDocumentPart mainDocumentPart = doc.getMainDocumentPart();
    //        cleanDocumentPart(mainDocumentPart,null); // 清理文档部分的代码被注释掉了
            // 从文档的XML内容中提取占位符
             placeholders = extractXMLPlaceholders(mainDocumentPart.getXML());
        // 显式释放引用,帮助垃圾回收
        mainDocumentPart = null;
        } finally {
            //清理缓存
            releaseDocxResources(doc,null);
            System.gc();
            System.runFinalization();

        }

        return placeholders; // 返回提取到的占位符集合
    }

    /**
     * cleanDocumentPart
     *
     * @param docPart
     */
    public static  boolean cleanDocumentPart(MainDocumentPart docPart,Map<String, String> map) throws Exception {
        if (docPart == null) {
            return false;
        }
        Document doc = docPart.getContents();
        String wmlTemplate = XmlUtils.marshaltoString(doc, true, false, Context.jc);
        doc = (Document) XmlUtils.unwrap(DocxVariableClearUtils.doCleanDocumentPart(wmlTemplate, Context.jc,map));
        docPart.setContents(doc);
        return true;
    }


    /**
     * 强制释放docx4j相关资源,切断引用链
     * @param doc Word文档对象
     */
    private static void releaseDocxResources(WordprocessingMLPackage doc, MainDocumentPart mainDocumentPart) {
        try {
            // 1. 清理主文档部分引用
            if (mainDocumentPart != null) {
                mainDocumentPart.setContents(null); // 清空文档内容
                mainDocumentPart = null; // 切断引用
            }
            // 2. 清理文档对象引用(关键:docx4j内部有大量缓存)
            if (doc != null) {
                // 清理包内的所有parts
                doc.getParts().getParts().clear();
                // 清理内容
                doc.getMainDocumentPart().setContents(null);
                // 切断包的引用
                doc = null;
            }

        } catch (Exception e) {
            logger.warn("释放docx4j资源时发生警告", e);
        }
    }


     public static void main(String[] args) throws Exception {

        Date date = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd-HHmmss").format(date);
        String mobanPath = "F:\\temp.docx";
        String outPath = "F:\\temp\\" + dateStr + ".docx";
        InputStream in = new FileInputStream(mobanPath);
        Map<String, String> signConfigMap = new HashMap<>();
        // 注意:以下书签名称需与模板中的实际书签名称一致
        signConfigMap.put("CC1", "张三 2025-01-01");
        signConfigMap.put("CC2", "李四 2025-02-02");
        signConfigMap.put("AAA", "王五 2025-01-05");
        signConfigMap.put("BBB", "赵六 2025-01-06");

        FileOutputStream out  = new FileOutputStream(outPath);
        in = new FileInputStream(mobanPath);
        Docx4jUtils.replaceDocUseDoc4j(in,signConfigMap, out);
        out.close();
    }
}

============================================================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值