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