基于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();
}
}
============================================================

1700

被折叠的 条评论
为什么被折叠?



