POI解析word文件,并为特定规则的key替换值

转载: https://www.aliyun.com/jiaocheng/778166.html

模板替换内容key是: ${enforcername1}


package com.jsy.test.pdf;


import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;


public class WordReplace {


/*
* 在Word文档中段落的最小的操作单位是XWPFRun,正常的一个段落,会被分割成多个小的XWPFRun,这些XWPFRun组合在一起就是一个完整的段落。
*
*
* 通常我们在Word文档中做的标记${mark_1},在文档中会被分割成多个XWPFRun,所以我们没法使用一个XWPFRun来进行标记文本替换。在这里,
* 我们想到一个方法,就是使用类似于找到字符串中子串下标的方法,找到段落XWPFRun中子Run下标,记录起始和终止下标,在终止下标后insertNewRun
* (int pos),然后再从终止下标往前xwpfParagraph.removeRun(i);到起始下标。
*
*
* 这个方法可以以整个段落位单位进行标记文本替换。然后遍历文档中所有的段落进行替换。 全部代码如下:
*/


/**
* 替换所有段落中的标记
*
* @param xwpfParagraphList
* @param params
*/
public static void replaceInAllParagraphs(List<XWPFParagraph> xwpfParagraphList, Map<String, String> params) {
for (XWPFParagraph paragraph : xwpfParagraphList) {
if (paragraph.getText() == null || paragraph.getText().equals(""))
continue;
for (String key : params.keySet()) {
if (paragraph.getText().contains(key)) {
System.err.println("旧值: "+key);
replaceInParagraph(paragraph, key, params.get(key));
}
}
}
}


/**
* 替换段落中的字符串
*
* @param xwpfParagraph
* @param oldString
* @param newString
*/
public static void replaceInParagraph(XWPFParagraph xwpfParagraph, String oldString, String newString) {
Map<String, Integer> pos_map = findSubRunPosInParagraph(xwpfParagraph, oldString);
System.err.println(pos_map.toString());
if (pos_map != null) {
System.out.println("start_pos:" + pos_map.get("start_pos"));
System.out.println("end_pos:" + pos_map.get("end_pos"));
List<XWPFRun> runs = xwpfParagraph.getRuns();
XWPFRun modelRun = runs.get(pos_map.get("end_pos"));
XWPFRun xwpfRun = xwpfParagraph.insertNewRun(pos_map.get("end_pos") + 1);
System.err.println(newString);
xwpfRun.setText(newString);

System.out.println("字体大小:" + modelRun.getFontSize());
if (modelRun.getFontSize() != -1)
xwpfRun.setFontSize(modelRun.getFontSize());// 默认值是五号字体,但五号字体getFontSize()时,返回-1
xwpfRun.setFontFamily(modelRun.getFontFamily());
for (int i = pos_map.get("end_pos"); i >= pos_map.get("start_pos"); i--) {
System.out.println("remove run pos in :" + i);
xwpfParagraph.removeRun(i);
}
}
}


/**
* 找到段落中子串的起始XWPFRun下标和终止XWPFRun的下标
*
* @param xwpfParagraph
* @param substring
* @return
*/
public static Map<String, Integer> findSubRunPosInParagraph(XWPFParagraph xwpfParagraph, String substring) {
List<XWPFRun> runs = xwpfParagraph.getRuns();
int start_pos = 0;
int end_pos = 0;
String subtemp = "";
for (int i = 0; i < runs.size(); i++) {
subtemp = "";
start_pos = i;
for (int j = i; j < runs.size(); j++) {
if (runs.get(j).getText(runs.get(j).getTextPosition()) == null)
continue;
subtemp += runs.get(j).getText(runs.get(j).getTextPosition());
if (subtemp.equals(substring)) {
end_pos = j;
Map<String, Integer> map = new HashMap<>();
map.put("start_pos", start_pos);
map.put("end_pos", end_pos);
return map;
}
}
}
return null;
}


// 对表格中标记文本的替换
// 有些标记做在表格单元格中,每个单元格中的内容都是一个普通的段落,所以,我们只需遍历出所有的单元格,然后遍历出每个单元格中的所有段落,再调用以上方法进行标记文本替换即可。代码如下


/**
* 替换所有的表格
*
* @param xwpfTableList
* @param params
*/
public static void replaceInTables(List<XWPFTable> xwpfTableList, Map<String, String> params) {
for (XWPFTable table : xwpfTableList) {
replaceInTable(table, params);
}
}


/**
* 替换一个表格中的所有行
*
* @param xwpfTable
* @param params
*/
public static void replaceInTable(XWPFTable xwpfTable, Map<String, String> params) {
List<XWPFTableRow> rows = xwpfTable.getRows();
replaceInRows(rows, params);
}


/**
* 替换表格中的一行
*
* @param rows
* @param params
*/
public static void replaceInRows(List<XWPFTableRow> rows, Map<String, String> params) {
for (int i = 0; i < rows.size(); i++) {
XWPFTableRow row = rows.get(i);
replaceInCells(row.getTableCells(), params);
}
}


/**
* 替换一行中所有的单元格
*
* @param xwpfTableCellList
* @param params
*/
public static void replaceInCells(List<XWPFTableCell> xwpfTableCellList, Map<String, String> params) {
for (XWPFTableCell cell : xwpfTableCellList) {
replaceInCell(cell, params);
}
}


/**
* 替换表格中每一行中的每一个单元格中的所有段落
*
* @param cell
* @param params
*/
public static void replaceInCell(XWPFTableCell cell, Map<String, String> params) {
List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
replaceInAllParagraphs(cellParagraphs, params);
}


// 调用方法测试
public static void main(String[] args) throws IOException, Exception {
// TODO Auto-generated method stub
String filepathString = "C:\Users\Administrator\Desktop\现场笔录.docx";
String destpathString = "C:\Users\Administrator\Desktop\现场笔录_new.docx";
Map<String, String> map = new HashMap<String, String>();
map.put("${enforcername1}", "小白鼠");
map.put("${enforcername2}", "喵喵喵");
map.put("${drivername}", "卡特琳娜");
map.put("${driverphone}", "15112345678");


OPCPackage pack = POIXMLDocument.openPackage(filepathString);
XWPFDocument document = new XWPFDocument(pack);
/**
* 对段落中的标记进行替换
*/
List<XWPFParagraph> parasList = document.getParagraphs();
replaceInAllParagraphs(parasList, map);
/**
* 对表格中的标记进行替换
*/
List<XWPFTable> tables = document.getTables();
replaceInTables(tables, map);
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(destpathString);
document.write(outStream);
outStream.flush();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}


}



 

个人感觉有点慢,15KB的文件需要750ms,希望如果有大佬有好的方案可以评论区告诉我!因为我这个还要转PDF然后立即预览的,所以尽量缩减时间.

我现在的方法网页预览需要5164毫秒.

原文地址:https://www.cnblogs.com/yxgmagic/p/9851367.html