Java使用POI通过模板生成Word
前言
最近工作需要用到,所以记录下来以便查找。
一、概述
POI读写word使用的核心类是XWPFDocument。一个XWPFDocument代表一个docx文档,其可以用来读docx文档,也可以用来写docx文档。
主要包含下面这几种对象:
- XWPFParagraph:代表一个段落。
- XWPFRun:代表具有相同属性的一段文本。
- XWPFTable:代表一个表格。
- XWPFTableRow:表格的一行。
- XWPFTableCell:表格对应的一个单元格。
引用一段写的很好的结构说明:
1、poi之word文档结构介绍之正文段落
一个文档包含多个段落,一个段落包含多个Runs,一个Runs包含多个Run,Run是文档的最小单元
获取所有段落:
List<XWPFParagraph> paragraphs = word.getParagraphs();获取一个段落中的所有Runs:
List<XWPFRun> xwpfRuns = xwpfParagraph.getRuns();获取一个Runs中的一个Run:
XWPFRun run = xwpfRuns.get(index);2、poi之word文档结构介绍之正文表格
一个文档包含多个表格,一个表格包含多行,一行包含多列(格),每一格的内容相当于一个完整的文档
获取所有表格:
List<XWPFTable> xwpfTables = doc.getTables();获取一个表格中的所有行:
List<XWPFTableRow> xwpfTableRows = xwpfTable.getRows();获取一行中的所有列:
List<XWPFTableCell> xwpfTableCells = xwpfTableRow.getTableCells();获取一格里的内容:
List<XWPFParagraph> paragraphs = xwpfTableCell.getParagraphs();之后和正文段落一样
注:
- 表格的一格相当于一个完整的docx文档,只是没有页眉和页脚。里面可以有表格,使用
xwpfTableCell.getTables()获取。- 在poi文档中段落和表格是完全分开的,如果在两个段落中有一个表格,在poi中是没办法确定表格在段落中间的。(当然除非你本来知道了,这句是废话)。只有文档的格式固定,才能正确的得到文档的结构
3、poi之word文档结构介绍之页眉:
一个文档可以有多个页眉(不知道怎么会有多个页眉。。。),页眉里面可以包含段落和表格
获取文档的页眉:
List<XWPFHeader> headerList = doc.getHeaderList();获取页眉里的所有段落:
List<XWPFParagraph> paras = header.getParagraphs();获取页眉里的所有表格:
List<XWPFTable> tables = header.getTables();之后就一样了
4、poi之word文档结构介绍之页脚:
页脚和页眉基本类似,可以获取表示页数的角标
二、用例测试
先使用代码去达到对word的读和写。
数据准备
编写一个word文档如下:

源文件地址:
1- 读
/**
* @Description 测试读取word文件
* @Author hacah
* @Date 2021/6/15 14:45
*/
public static void testReadWord() throws IOException {
// 填写输入流
XWPFDocument xwpfDocument = new XWPFDocument(new FileInputStream("E:\\OneDrive\\文档\\自我介绍.docx"));
// 获取文本数据
List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
for (XWPFRun run : paragraph.getRuns()) {
System.out.println(run.text());
}
}
// 获取表格数据
List<XWPFTable> tables = xwpfDocument.getTables();
for (XWPFTable table : tables) {
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
List<XWPFTableCell> tableCells = row.getTableCells();
for (XWPFTableCell tableCell : tableCells) {
List<XWPFParagraph> paragraphs1 = tableCell.getParagraphs();
for (XWPFParagraph xwpfParagraph : paragraphs1) {
List<XWPFRun> runs = xwpfParagraph.getRuns();
for (XWPFRun run : runs) {
System.out.println(run);
}
}
}
}
}
}
public static void main(String[] args) throws IOException {
testReadWord();
}
结果:

2- 写
/**
* @Description 测试使用模板写入数据,新建数据后的word
* @author hacah
* @Date 2021/6/15 15:09
*/
public static void testWriteWord() throws IOException {
XWPFDocument xwpfDocument = new XWPFDocument(new FileInputStream("E:\\OneDrive\\文档\\自我介绍.docx"));
FileOutputStream fileOutputStream = new FileOutputStream("E:\\OneDrive\\文档\\通过模板创建的数据.docx");
// 获取第一段文件的第一个run
XWPFRun xwpfRun = xwpfDocument.getParagraphs().get(0).getRuns().get(0);
xwpfRun.setText("修改的数据");
xwpfDocument.write(fileOutputStream);
fileOutputStream.close();
}
public static void main(String[] args) throws IOException {
testWriteWord();
}
结果:

三、案例
使用未修改过的上文的原word的文件。对${}的数据进行替换,对空表格插入数据。
代码如下:
package com.hacah.word;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.poi.util.StringUtil;
import org.apache.poi.xwpf.usermodel.*;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Hacah
* @className: templateToWord
* @description: TODO 类描述
* @date 2021/6/15 15:17
*/
public class templateToWord {
/**
* @param wordValue ${...} 带${}的变量
* @param map 存储需要替换的数据
* @return java.lang.String
* @Description 有${}的值匹配出替换的数据,没有${}就返回原来的数据
* @author hacah
* @Date 2021/6/15 16:02
*/
public static String matchesValue(String wordValue, Map<String, String> map) {
for (String s : map.keySet()) {
String s1 = new StringBuilder("${").append(s).append("}").toString();
if (s1.equals(wordValue)) {
wordValue = map.get(s);
}
}
return wordValue;
}
/**
* @return boolean
* @Description 测试是否包含需要替换的数据
* @author hacah
* @Date 2021/6/15 15:30
*/
public static boolean isReplacement(String text) {
boolean check = false;
if (text.contains("$")) {
check = true;
}
return check;
}
/**
* @Description 处理所有文段数据,除了表格
* @param xwpfDocument
* @param insertTextMap
* @author hacah
* @Date 2021/6/17 10:04
*/
public static void handleParagraphs(XWPFDocument xwpfDocument, Map<String, String> insertTextMap) {
for (XWPFParagraph paragraph : xwpfDocument.getParagraphs()) {
String text = paragraph.getText();
if (isReplacement(text)) {
for (XWPFRun run : paragraph.getRuns()) {
// 判断带有${}的run
// System.out.println(run);
run.setText(matchesValue(run.text(), insertTextMap), 0);
}
}
}
}
/**
* @Description 通过word模板生成word的主方法
* @param inputStream
* @param outputStream
* @param insertTextMap
* @param addList
* @author hacah
* @Date 2021/6/17 10:03
*/
public static void generateWord(InputStream inputStream, OutputStream outputStream, Map<String, String> insertTextMap, List<String[]> addList) throws IOException {
//获取docx解析对象
XWPFDocument xwpfDocument = new XWPFDocument(inputStream);
// 处理所有文段数据,除了表格
handleParagraphs(xwpfDocument, insertTextMap);
// 处理表格数据
handleTable(xwpfDocument, insertTextMap, addList);
// 写出数据
xwpfDocument.write(outputStream);
outputStream.close();
}
/**
* @Description 处理表格数据方法
* @param xwpfDocument
* @param insertTextMap
* @param addList
* @author hacah
* @Date 2021/6/17 10:04
*/
public static void handleTable(XWPFDocument xwpfDocument, Map<String, String> insertTextMap, List<String[]> addList) {
List<XWPFTable> tables = xwpfDocument.getTables();
for (XWPFTable table : tables) {
List<XWPFTableRow> rows = table.getRows();
if (rows.size() > 1) {
if (isReplacement(table.getText())) {
// 替换数据
for (XWPFTableRow row : rows) {
List<XWPFTableCell> tableCells = row.getTableCells();
for (XWPFTableCell tableCell : tableCells) {
if (isReplacement(tableCell.getText())) {
// 替换数据
List<XWPFParagraph> paragraphs = tableCell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
run.setText(matchesValue(tableCell.getText(), insertTextMap), 0);
}
}
}
}
}
} else {
// 插入数据
for (int i = 1; i < addList.size(); i++) {
XWPFTableRow row = table.createRow();
}
List<XWPFTableRow> rowList = table.getRows();
for (int i = 1; i < rowList.size(); i++) {
XWPFTableRow xwpfTableRow = rowList.get(i);
List<XWPFTableCell> tableCells = xwpfTableRow.getTableCells();
for (int j = 0; j < tableCells.size(); j++) {
XWPFTableCell xwpfTableCell = tableCells.get(j);
xwpfTableCell.setText(addList.get(i - 1)[j]);
}
}
}
}
}
}
public static void main(String[] args) throws IOException {
// 替换文本数据构建
HashMap<String, String> insertTextMap = new HashMap<>(16);
insertTextMap.put("like", "[爱好test]");
insertTextMap.put("birthday", "[生日test]");
insertTextMap.put("sex", "[性别test]");
insertTextMap.put("name", "[姓名test]");
insertTextMap.put("age", "[年龄test]");
// 插入数据构建
ArrayList<String[]> addList = new ArrayList<>();
addList.add(new String[]{"【1插入电话】", "【2插入地址】", "【3插入邮箱】"});
addList.add(new String[]{"【test插入电话2】", "【test插入地址2】", "【test插入邮箱2】"});
// 设置模板输入和结果输出
FileInputStream fileInputStream = new FileInputStream("E:\\OneDrive\\文档\\自我介绍.docx");
FileOutputStream fileOutputStream = new FileOutputStream("E:\\OneDrive\\文档\\通过模板创建的数据.docx");
// 生成word
generateWord(fileInputStream, fileOutputStream, insertTextMap, addList);
}
}
结果:

参考或相关文章
https://www.iteye.com/blog/elim-2049110
https://www.jianshu.com/p/6603b1ea3ad1

本文介绍了如何使用Java的POI库通过模板生成Word文档,详细讲解了POI操作Word的基本元素,如段落、表格、页眉页脚,并提供了读写Word的测试用例及实际案例。
5662

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



