Java poi XWPFDocument 操作2007Word,实现参数替换、动态插入表格数据


最近接到个需求,需要将数据导出生成word文件,刚开始是打算通过操作poi生成一个word文件的,但是发现太麻烦了,在网上查了一圈,发现可以通过模板替换的形式就能实现效果。于是根据自身的实际情况做了一个.docx的word模板,如下
在这里插入图片描述
因为word模板文件是2007版.docx格式的,所以使用 XWPFDocument 对文档进行操作,若是03版后缀名为.doc的,就得使用HWPFDocument了(需要单独引入依赖,这里以07版演示)

在pom文件中引入依赖

<dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>4.1.0</version>
</dependency>

添加Word工具类

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.*;

import java.io.*;
import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WordUtils {
    /** * 对word进行参数替换并生成新的文件 * * @param inputPath word模板路径和名称 * @param outPath word模板路径和名称 * @param params 待填充的数据 params.put("key",value) 文档中对应为 ${key} */
    public static void readwriteWord(String inputPath, String outPath, Map<String, Object> params) throws IOException, InvalidFormatException {

        InputStream is = new FileInputStream(inputPath);
        XWPFDocument document = new XWPFDocument(OPCPackage.open(is));
        replaceParams(document, params);
        OutputStream os = new FileOutputStream(outPath);
        document.write(os);
        close(os);
        close(is);
    }

    /** * 替换word中的参数与动态新增表格行数 * * @param in 模板输入流 * @param os 修改后的输出流 * @param params 待填充的数据 params.put("key",value) 文档中对应为 ${key} * @param tableIndex 表格下标,从0开始,如Word中3个表格,仅对1、3个表格进行修改,参数为 int[]{0,2} * @param tables 新增表格的数据,数据格式为 [表[行[单元格]]] * @throws InvalidFormatException * @throws IOException */
    public static void readwriteWord(InputStream in, OutputStream os, Map<String, Object> params, int[] tableIndex, List<List<String[]>> tables) throws IOException, InvalidFormatException {
        XWPFDocument document = new XWPFDocument(OPCPackage.open(in));
        replaceParams(document, params);
        if (tableIndex.length != tables.size()) throw new RemoteException("表格下标数量与表格参数数量不一致!");
        for (int i = 0; i < tableIndex.length; i++) {
            changeTable(document, params, i, tables.get(i));
        }
        document.write(os);
    }

    /** * 实现对word读取和修改操作 * * @param in 模板输入流 * @param out 输出流 * @param params 待填充的数据,从数据库读取 * @throws IOException * @throws InvalidFormatException */
    public static void readwriteWord(InputStream in, OutputStream out, Map<String, Object> params) {
        try {
            XWPFDocument document;
            document = new XWPFDocument(OPCPackage.open(in));
            replaceParams(document, params);
            document.write(out);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /** * 替换段落里面的变量 * * @param doc 要替换的文档 * @param params 参数 */
    private static void replaceParams(XWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph paragraph;
        while (iterator.hasNext()) {
            paragraph = iterator.next();
            replaceParam(paragraph, params);
        }
    }

    /** * @param doc docx解析对象 * @param params 需要替换的信息集合 * @param tableIndex 第几个表格 * @param tableList 需要插入的表格信息集合 */
    public static void changeTable(XWPFDocument doc, Map<String, Object> params, int tableIndex, List<String[]> tableList) {
        //获取表格对象集合
        List<XWPFTable> tables = doc.getTables();
        //获取第一个表格 根据实际模板情况 决定去第几个word中的表格
        XWPFTable table = tables.get(tableIndex);
        //替换表格中的参数
        replaceTableParams(doc, params);
        //在表格中插入数据
        insertTable(table, tableList);
    }

    /** * 替换表格里面的变量 * * @param doc 要替换的文档 * @param params 参数 */
    private static void replaceTableParams(XWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        while (iterator.hasNext()) {
            table = iterator.next();
            //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
            if (matcher(table.getText()).find()) {
                rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        paras = cell.getParagraphs();
                        for (XWPFParagraph para : paras) {
                            replaceParam(para, params);
                        }
                    }
                }
            }

        }
    }

    /** * 为表格插入行数,此处不处理表头,所以从第二行开始 * * @param table 需要插入数据的表格 * @param tableList 插入数据集合 */
    private static void insertTable(XWPFTable table, List<String[]> tableList) {
        //创建与数据一致的行数
        for (int i = 0; i < tableList.size(); i++) {
            table.createRow();
        }
        int length = table.getRows().size();
        for (int i = 1; i < length; i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                cell.setText(tableList.get(i - 1)[j]);
            }
        }
    }

    /** * 替换段落里面的变量 * * @param paragraph 要替换的段落 * @param params 参数 */
    private static void replaceParam(XWPFParagraph paragraph, Map<String, Object> params) {
        List<XWPFRun> runs;
        Matcher matcher;
        String runText = "";

        if (matcher(paragraph.getParagraphText()).find()) {
            runs = paragraph.getRuns();
            int j = runs.size();
            for (int i = 0; i < j; i++) {
                runText += runs.get(0).toString();
                //保留最后一个段落,在这段落中替换值,保留段落样式
                if (!((j - 1) == i)) {
                    paragraph.removeRun(0);
                }
            }
            matcher = matcher(runText);
            if (matcher.find()) {
                while ((matcher = matcher(runText)).find()) {
                    runText = matcher.replaceFirst(String.valueOf(params.get(matcher.group(1))));
                }
                runs.get(0).setText(runText, 0);
            }
        }

    }

    /** * 正则匹配字符串 * * @param str * @return */
    private static Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /** * 关闭输入流 * * @param is */
    private static void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /** * 关闭输出流 * * @param os */
    private static void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

测试

import java.io.*;
import java.util.*;

/** * @author f * @date 2021-05-06 23:05 */
public class WordTest {
    public static void main(String[] args) {

        Map<String, Object> params = new HashMap<>();
        params.put("name", "XXXWord");
        params.put("startDate", "2021-04-21");
        params.put("endDate", "2021-04-28");
        params.put("header1", "表头1");
        params.put("header2", "表头2");
        params.put("header3", "表头3");

        List<List<String[]>> tableList = new ArrayList<>();

        List<String[]> table1 = new ArrayList<>();
        table1.add(new String[]{"1","张三","男","22","186xxxxxxxx","北"});
        table1.add(new String[]{"2","李四","女","23","187xxxxxxxx","上"});
        table1.add(new String[]{"3","王五","男","24","188xxxxxxxx","广"});
        table1.add(new String[]{"4","赵六","女","25","189xxxxxxxx","深"});

        List<String[]> table2 = new ArrayList<>();
        table2.add(new String[]{"1","鹅厂","T3","xxx","5"});
        table2.add(new String[]{"2","菊厂","18","xxx","5"});
        table2.add(new String[]{"3","动物园","P8","xxx","5"});

        List<String[]> table3 = new ArrayList<>();
        table3.add(new String[]{"1","a1","a2","a3"});
        table3.add(new String[]{"2","b1","b2","b3"});
        table3.add(new String[]{"3","c1","c2","c3"});

        tableList.add(table1);
        tableList.add(table2);
        tableList.add(table3);

        String filePath = "D:\\word模板.docx";
        String outpath = "D:\\word文件.docx";
        try {
            InputStream is = new FileInputStream(filePath);
            OutputStream os = new FileOutputStream(outpath);
            WordUtils.readwriteWord(is,os,params,new int[]{0,1,2},tableList);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

生成的文件内容如下
在这里插入图片描述

因XWPFRun 获取的数据有时候会有问题,不能完整的识别参数 ${xxx},因此先把一段的字符全部读取,再用正则表达式去匹配替换

参考文档
https://www.jianshu.com/p/6603b1ea3ad1


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM