最近在开发中遇到一个客户提的需求:Word模板定制,就是说上传一个Word文档,以后就从这个文档进行数据的套打。
逛了一圈发现哪种实现方案都不是太完美。
方案一:
POI 实现Word替换书签_poi 替换word书签-CSDN博客 这个方案实现了使用书签进行替换,但最后格式也被消除了。
方案二:
POI 实现Word替换文本2种情况(正常文本、表格文本)_poi替换word内容-CSDN博客 这个方案实现了使用关键字进行替换,是可行的。但我觉得怪怪的,输入关键字的时候必须保证一次性输入,虽然不是什么难事,但只能习惯了就好。
方案三:
使用Spire.Doc for Java 可以看这篇
Java 替换 Word 书签内容
这个我也验证了是可行的,但是有个缺点就是《收费》白嫖党的大失败。
当然你也可以使用免费版,然后会出现这个
你也可以使用技术手段对生成后的文件进行二次编辑。
哎。。。。都太不好使最后我自己结合GPT还有方案二搓了一个,能实现doc 和 docx文件的关键字替换。
迫不得已方案四:
依赖
<!-- docx & doc 文件处理依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.3</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
</dependencies>
自己写的工具类:
package com.mrzeng.code;
import cn.hutool.core.util.StrUtil;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.CharacterRun;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Microsoft Word Util
*/
public class MicrosoftWordUtil {
/**
* docx 文件替换 ${text}
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void replaceDocxFields(Map<String, String> textMap, XWPFDocument document) {
for (Map.Entry<String, String> entry : textMap.entrySet()) {
String placeholder = "${" + entry.getKey() + "}";
String replacement = entry.getValue();
for (XWPFParagraph paragraph : document.getParagraphs()) {
for (XWPFRun run : paragraph.getRuns()) {
String text = run.getText(0);
if (text != null && text.contains(placeholder)) {
text = text.replace(placeholder, replacement);
run.setText(text, 0);
}
}
}
}
changeTable(document, textMap);
}
/**
* doc 文件替换 ${text}
*
* @param document doc解析对象
* @param textMap 需要替换的信息集合
*/
public static void replaceDocFields(Map<String, String> textMap, HWPFDocument document) {
Range range = document.getRange();
for (Map.Entry<String, String> entry : textMap.entrySet()) {
String placeholder = "${" + entry.getKey() + "}";
String replacement = entry.getValue();
for (int i = 0; i < range.numParagraphs(); i++) {
Paragraph paragraph = range.getParagraph(i);
for (int j = 0; j < paragraph.numCharacterRuns(); j++) {
CharacterRun run = paragraph.getCharacterRun(j);
String text = run.text();
if (text.contains(placeholder)) {
text = text.replace(placeholder, replacement);
run.replaceText(text, false);
}
}
}
}
}
/**
* docx 替换表格对象方法
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
private static void changeTable(XWPFDocument document, Map<String, String> textMap) {
// 获取表格对象集合
List<XWPFTable> tables = document.getTables();
for (XWPFTable table : tables) {
// 判断表格内容是否可以替换
String cellText = table.getText();
if (StrUtil.isNotBlank(cellText) && cellText.contains("${")) {
List<XWPFTableRow> rows = table.getRows();
// 遍历表格,并替换模板
eachTable(rows, textMap);
}
}
}
/**
* docx 遍历表格
*
* @param rows 表格行对象
* @param textMap 需要替换的信息集合
*/
private static void eachTable(List<XWPFTableRow> rows, Map<String, String> textMap) {
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
// 判断单元格内容是否可以替换
String cellText = cell.getText();
if (StrUtil.isNotBlank(cellText) && cellText.contains("${")) {
List<XWPFParagraph> paragraphs = cell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
run.setText(changeValue(run.toString(), textMap), 0);
}
}
}
}
}
}
/**
* docx 匹配传入信息集合与模板
*
* @param value 模板需要替换的区域
* @param textMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
private static String changeValue(String value, Map<String, String> textMap) {
Set<Entry<String, String>> textSets = textMap.entrySet();
for (Entry<String, String> textSet : textSets) {
// 匹配模板与替换值 格式${key}
String key = "${" + textSet.getKey() + "}";
if (value.contains(key)) {
value = value.replace(key, textSet.getValue());
}
}
return value;
}
}
测试用的测试类:
import com.mrzeng.code.MicrosoftWordUtil;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class ReplaceWordTest {
/**
* 测试doc
*
* @throws IOException
*/
@Test
public void DocReplaceWordsTest() throws IOException {
FileInputStream fileInputStream = new FileInputStream("input.doc");
HWPFDocument document = new HWPFDocument(fileInputStream);
Map<String, String> bookTagMap = new HashMap<>();
bookTagMap.put("school", "清华大学");
bookTagMap.put("name", "张三");
MicrosoftWordUtil.replaceDocFields(bookTagMap, document);
// 保存文档到 output.docx
FileOutputStream fileOutputStream = new FileOutputStream("output.doc");
document.write(fileOutputStream);
}
/**
* 测试docx
*
* @throws IOException
*/
@Test
public void DocxReplaceWordsTest() throws IOException {
FileInputStream fileInputStream = new FileInputStream("input.docx");
XWPFDocument document = new XWPFDocument(fileInputStream);
Map<String, String> bookTagMap = new HashMap<>();
bookTagMap.put("school", "清华大学");
bookTagMap.put("name", "张三");
MicrosoftWordUtil.replaceDocxFields(bookTagMap, document);
// 保存文档到 output.docx
FileOutputStream fileOutputStream = new FileOutputStream("output.docx");
document.write(fileOutputStream);
}
}
最后测试需要的word文档中的内容可以参考图片:
这个现在也只能这样了,如果哪天有新的想法再说吧。