基於POI的wod模板文件,導入參數,導出最終文件

最近工作上碰到了這個問題,就研究了一下。

結合了網上幾位大哥的成果,我自己又優化了一下。

除了基礎的導入參數之外,還優化了參數識別能力,添加了頁面複製能力,並且保留了樣式。

下面是我測試的word模板:

你好,現在是${time} ,我是${userName} 。

表格1

##{foreachTable}##

table1

序號

姓名

年齡

##{foreachRows}##

${number}

${name}

${age}${unit}

 

第二頁,整個頁面複製的模板 ,其實就是用一個上面的表格,然後把第三行內容行單元格的邊隱藏掉就行了。

##{foreachTable}##

page1

##{foreachRows}##

頁面複製測試。

本頁介紹${name},詳情看下錶:

姓名

${name}

年齡

${age}

思想派別

${philosophy}

能力

${skill}

 

先是maven的依賴包:

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

然後是我的代碼,工具類的代碼:

package com.zyy.testPro.word;

import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;

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

/**
 * @ClassName:
 * @Description: POI的word文檔模板工具類
 * @CreateDate: 2020-01-08 14:39
 * @Author:zhaoyangyang
 */
public class WordTemplateUtils {


    /**
     * 根據模板生成word
     * @param inputUrl     模板的路徑
     * @param params   需要替換的參數
     */
    public static void getWord(String inputUrl,String outputUrl, Map<String, Object> params){
        try {
            XWPFDocument doc = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));

            @SuppressWarnings("unchecked")
            Map<String, Object> parametersMap = (Map<String, Object>) params.get("parametersMap");
            replaceInPara(doc, parametersMap);    //替換文本里面的變量
            replaceInTable(doc, params); //替換表格裏面的變量
            OutputStream os = new FileOutputStream(outputUrl);
            doc.write(os);
            close(os);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    private static List<XWPFRun> getFormatRuns(XWPFParagraph paragraph){
        //遍歷獲取段落中所有的runs
        List<XWPFRun> runs = paragraph.getRuns();
        //合併邏輯
        for (Integer i = 0; i < runs.size(); i++) {
            String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
            if (text0 != null && text0.contains("$")) {
                if((text0.length()>text0.indexOf("$")+1 && '{'==text0.charAt(text0.indexOf("$")+1)) || (runs.get(i+1)!=null && runs.get(i+1).getText(-1).startsWith("{"))){
                    //記錄分隔符中間跨越的runs數量,用於字符串拼接和替換
                    int j = i;
                    boolean flag=false;
                    for (; j < runs.size(); j++) {
                        String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
                        if (text1 != null && text1.contains("}")) {
                            flag=true;
                            break;
                        }
                    }



                    if (flag) {
                        //將中間設計的run拼成一個大字符串,並將所有run設爲null
                        StringBuilder newText = new StringBuilder();
                        for (int k = i; k <= j; k++) {
                            String text2 = runs.get(k).text();
                            newText.append(text2);
                            runs.get(k).setText(null, 0);
                        }

                        //處理這種以${結尾的情況,不然這個會丟失
                        if(newText.toString().trim().endsWith("${")){
                            if(runs.size()>j){
                                runs.get(j+1).setText("${"+runs.get(j+1).text(),0);
                            }
                        }

                        //將新字符串分割爲理想中的替換字符串和普通字符串
                        String[] newArray= newText.toString().split("\\$\\{");
                        for(String str1:newArray){
                            if(str1.contains("}")){//包含替換字符串
                                String[] newArray2= str1.split("}");
                                if(str1.startsWith("}")){//替換字符串爲空,}後面可能跟有普通字符串
                                    if(newArray2.length>0){
                                        XWPFRun run=paragraph.insertNewRun(i++);
                                        run.setText(newArray2[0]);
                                        run.getCTR().setRPr(runs.get(i).getCTR().getRPr());
                                    }
                                }else{//替換字符串不爲空,可能有普通字符牀
                                    if(newArray2.length>1){
                                        XWPFRun run1=paragraph.insertNewRun(i++);
                                        run1.setText("${"+newArray2[0]+"}");
                                        run1.getCTR().setRPr(runs.get(i).getCTR().getRPr());

                                        XWPFRun run2=paragraph.insertNewRun(i++);
                                        run2.setText(newArray2[1]);
                                        run2.getCTR().setRPr(runs.get(i).getCTR().getRPr());
                                    }else{
                                        XWPFRun run=paragraph.insertNewRun(i++);
                                        run.setText("${"+newArray2[0]+"}");
                                        run.getCTR().setRPr(runs.get(i).getCTR().getRPr());
                                    }
                                }
                            }else{//普通字符串
                                XWPFRun run=paragraph.insertNewRun(i++);
                                run.setText(str1);
                                run.getCTR().setRPr(runs.get(i).getCTR().getRPr());
                            }
                        }

                    }
                }
            }
        }
        return runs;

    }

    /**
     * 替換段落裏面的變量
     * @param doc    要替換的文檔
     * @param params 參數
     */
    private static void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            replaceInPara(para, params);
        }
    }

    /**
     * 替換段落裏面的變量
     *
     * @param para   要替換的段落
     * @param params 參數
     */
    private static void replaceInPara(XWPFParagraph para, Map<String, Object> params) {
        List<XWPFRun> runs;
        if (matcher(para.getParagraphText()).find()) {
            runs = getFormatRuns(para);

            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                if(run!=null){
                    String runText = run.toString();
                    if (runText.length()>1 && '$' == runText.charAt(0) && '{' == runText.charAt(1)) {
                        String key=runText.replace("${","").replace("}","").trim();
                        Object value=params.get(key);
                        if (value instanceof String) {//文字
                            run.setText((String)value,0);

                        }else{
                            run.setText("",0);
                        }
                    }
                }
            }
        }
    }


    /**
     * 替換表格裏面的變量
     * @param doc    要替換的文檔
     * @param params 參數
     */
    private static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        @SuppressWarnings("unchecked")
        Map<String, Object> parametersMap = (Map<String, Object>) params
                .get("parametersMap");
        while (iterator.hasNext()) {
            table = iterator.next();
            String tableText = table.getText();
            List<XWPFTableCell> tableCells = table.getRows().get(0).getTableCells();// 獲取到模板表格第一行,用來判斷表格類型
            // 查找到##{foreach標籤,該表格需要處理循環
            if (tableText.indexOf("##{foreachTable}##") > -1) {//循環處理表格
                // 查找到##{foreach標籤,該表格需要處理循環
                if (tableCells.size() != 2
                        || tableCells.get(0).getText().indexOf("##{foreachTable}##") < 0
                        || tableCells.get(0).getText().trim().length() == 0) {
                    System.out
                            .println("文檔中有"
                                    + "表格模板錯誤,模板表格第一行需要設置2個單元格,"
                                    + "第一個單元格存儲表格類型(##{foreachTable}## 或者 ##{foreachTableRow}##),第二個單元格定義數據源。");
                    return;
                }
                String dataSource = tableCells.get(1).getText();
                System.out.println("讀取到數據源:"+dataSource);
                if (!params.containsKey(dataSource)) {
                    System.out.println("文檔中" + dataSource + "表格模板數據源缺失");
                    return;
                }
                @SuppressWarnings("unchecked")
                List<Map<String, Object>> tableDataList = (List<Map<String, Object>>) params
                        .get(dataSource);

                // System.out.println("循環生成表格內部的行");
                insertTable(table, tableDataList,parametersMap);  //插入數據

            }else if(matcher(tableText).find()){//靜態表格,替換數據
                replaceTable(table,parametersMap);
            }
        }
    }

    /**
     * 爲表格替換數據
     * @param table
     * @param parametersMap
     */
    private static void replaceTable(XWPFTable table, Map<String, Object> parametersMap){
        List<XWPFTableRow> rows = table.getRows();

        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                List<XWPFParagraph> paras = cell.getParagraphs();
                for (XWPFParagraph para : paras) {
                    replaceInPara(para, parametersMap);
                }
            }
        }
    }

    /**
     * 爲表格插入數據,行數不夠添加新行
     *
     * @param table     需要插入數據的表格
     * @param tableList 插入數據集合
     */
    private static void insertTable(XWPFTable table, List<Map<String,Object>> tableList, Map<String, Object> parametersMap) {

        table.removeRow(0);//刪除第一行

        List<XWPFTableRow> TempTableRows = table.getRows();// 獲取模板表格所有行

        int tagRowsIndex=0;
        for(int i=0;i<TempTableRows.size();i++){
            String rowText = TempTableRows.get(i).getCell(0).getText();// 獲取到表格行的第一個單元格
            if (rowText.indexOf("##{foreachRows}##") > -1) {
                tagRowsIndex = i;
                i++;//跳過下一行模板行。
            }else{
                replaceTableRow(TempTableRows.get(i), parametersMap);// 處理標籤替換
            }
        }

        /* 循環生成模板行 */
        XWPFTableRow tempRow = TempTableRows.get(tagRowsIndex + 1);// 獲取到模板行
        for (int i = tableList.size()-1; i >= 0; i--) {
            //指定位置,複製行
            XWPFTableRow newCreateRow=copyRow(table,tempRow,tagRowsIndex + 2);
            replaceTableRow(newCreateRow, tableList.get(i));// 處理標籤替換
        }
        table.removeRow(tagRowsIndex);//刪除標籤行
        table.removeRow(tagRowsIndex);//刪除模板行

    }


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

    /**
     * 將輸入流中的數據寫入字節數組
     *
     * @param in
     * @return
     */
    public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
        byte[] byteArray = null;
        try {
            int total = in.available();
            byteArray = new byte[total];
            in.read(byteArray);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isClose) {
                try {
                    in.close();
                } catch (Exception e2) {
                    e2.getStackTrace();
                }
            }
        }
        return byteArray;
    }


    /**
     * 關閉輸入流
     *
     * @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();
            }
        }
    }

    public static XWPFTableRow copyRow(XWPFTable table, XWPFTableRow sourceRow, int rowIndex){
        //在表格指定位置新增一行
        XWPFTableRow targetRow = table.insertNewTableRow(rowIndex);
        //複製行屬性
        targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
        List<XWPFTableCell> cellList = sourceRow.getTableCells();
        if (null == cellList) {
            return null;
        }
        //複製列及其屬性和內容
        for (XWPFTableCell sourceCell : cellList) {
            XWPFTableCell targetCell = targetRow.addNewTableCell();
            //列屬性
            targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
            //段落屬性
            if(sourceCell.getParagraphs()!=null&&sourceCell.getParagraphs().size()>0){
                List<IBodyElement> bodyElements = sourceCell.getBodyElements();// 所有對象(段落+表格)

                int curT = 0;// 當前操作表格對象的索引
                int curP = 0;// 當前操作段落對象的索引
                IBodyElement currentElement=targetCell.getParagraphs().get(0);
                for(int i=0;i<bodyElements.size();i++){
                    IBodyElement element=bodyElements.get(i);
                    if(BodyElementType.TABLE.equals(element.getElementType())){// 處理表格
                        //位置指針
                        XmlCursor cursor;
                        if(currentElement instanceof XWPFParagraph){
                            cursor=((XWPFParagraph) currentElement).getCTP().newCursor();
                        }else{
                            cursor=((XWPFTable)currentElement).getCTTbl().newCursor();
                        }
                        cursor.toNextSibling();

                        XWPFTable targetTable = targetCell.insertNewTbl(cursor);
                        XWPFTable sourceTable = element.getBody().getTables().get(curT);

                        //複製表格
                        copyTable(targetTable,sourceTable);

                        currentElement=targetTable;//更改當前元素
                        curT++;
                    }else{
                        XmlCursor cursor;
                        if(currentElement instanceof XWPFParagraph){
                            cursor=((XWPFParagraph) currentElement).getCTP().newCursor();
                        }else{
                            cursor=((XWPFTable)currentElement).getCTTbl().newCursor();
                        }
                        cursor.toNextSibling();
                        XWPFParagraph  targetParagraph=targetCell.insertNewParagraph(cursor);
                        XWPFParagraph  sourceParagraph=sourceCell.getParagraphs().get(curP);

                        //複製段落
                        targetParagraph.getCTP().setPPr(sourceParagraph.getCTP().getPPr());// 設置段落樣式
                        for (XWPFRun sourceRun : sourceParagraph.getRuns()) {
                            XWPFRun targetRun = targetParagraph.createRun();
                            targetRun.getCTR().setRPr(sourceRun.getCTR().getRPr());
                            // 設置文本
                            targetRun.setText(sourceRun.text());
                        }
                        currentElement=targetParagraph;//更改當前元素
                        curP++;
                    }
                }
            }else{
                targetCell.setText(sourceCell.getText());
            }
        }
        return targetRow;
    }

    /**
     * 複製表格
     * @param targetTable
     * @param sourceTable
     */
    public static void copyTable(XWPFTable targetTable, XWPFTable sourceTable) {
        targetTable.getCTTbl().setTblPr(sourceTable.getCTTbl().getTblPr());
        for(int i=0;i<sourceTable.getRows().size();i++){
            XWPFTableRow row=sourceTable.getRows().get(i);
            copyRow(targetTable,row,i);
        }

    }


    /**
     * 根據參數parametersMap對錶格的一行進行標籤的替換
     *
     * @author Juveniless
     * @date 2017年11月23日 下午2:09:24
     * @param tableRow
     *            表格行
     * @param parametersMap
     *            參數map
     *
     */
    public static void replaceTableRow(XWPFTableRow tableRow, Map<String, Object> parametersMap) {

        List<XWPFTableCell> tableCells = tableRow.getTableCells();
        for (XWPFTableCell xWPFTableCell : tableCells) {
            List<XWPFParagraph> paragraphs = xWPFTableCell.getParagraphs();
            for (XWPFParagraph xwpfParagraph : paragraphs) {
                if(xwpfParagraph.getText().indexOf("${")>-1){
                    replaceInPara(xwpfParagraph, parametersMap);
                }
            }
            List<XWPFTable> tables=xWPFTableCell.getTables();
            for (XWPFTable xwpfTable : tables) {
                replaceTable(xwpfTable,parametersMap);
            }
        }

    }




}

 測試類的代碼:

package com.zyy.testPro.word;

/**
 * @ClassName:
 * @Description:
 * @CreateDate: 2020-01-02 16:34
 * @Author:zhaoyangyang
 */


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

public class Test {

    public static void main(String[] args) throws IOException {

        Map<String, Object> wordDataMap = new HashMap<String, Object>();// 存儲報表全部數據
        Map<String, Object> parametersMap = new HashMap<String, Object>();// 存儲報表中不循環的數據


        parametersMap.put("time", "2020-1-14");
        parametersMap.put("userName", "valhalla");


        List<Map<String, Object>> table1 = new ArrayList<Map<String, Object>>();
        Map<String, Object> map1=new HashMap<>();
        map1.put("number", "1");
        map1.put("name", "孔子");
        map1.put("age", "71");
        map1.put("unit", "歲");
        Map<String, Object> map2=new HashMap<>();
        map2.put("number", "2");
        map2.put("name", "墨子");
        map2.put("age", "91");
        map2.put("unit", "歲");
        Map<String, Object> map3=new HashMap<>();
        map3.put("number", "3");
        map3.put("name", "莊子");
        map3.put("age", "83");
        map3.put("unit", "歲");
        Map<String, Object> map4=new HashMap<>();
        map4.put("number", "4");
        map4.put("name", "韓非子");
        map4.put("age", "47");
        map4.put("unit", "歲");

        table1.add(map1);
        table1.add(map2);
        table1.add(map3);
        table1.add(map4);

        wordDataMap.put("table1", table1);

        List<Map<String, Object>> page1 = new ArrayList<Map<String, Object>>();
        Map<String, Object> obj1=new HashMap<>();
        obj1.put("name", "孔子");
        obj1.put("age", "71");
        obj1.put("philosophy", "儒家");
        obj1.put("skill", "教學");
        Map<String, Object> obj2=new HashMap<>();
        obj2.put("name", "墨子");
        obj2.put("age", "91");
        obj2.put("philosophy", "墨家");
        obj2.put("skill", "木工");
        Map<String, Object> obj3=new HashMap<>();
        obj3.put("name", "莊子");
        obj3.put("age", "83");
        obj3.put("philosophy", "道家");
        obj3.put("skill", "講故事");
        Map<String, Object> obj4=new HashMap<>();
        obj4.put("name", "韓非子");
        obj4.put("age", "47");
        obj4.put("philosophy", "法家");
        obj4.put("skill", "權謀");
        page1.add(obj1);
        page1.add(obj2);
        page1.add(obj3);
        page1.add(obj4);

        wordDataMap.put("page1", page1);

        wordDataMap.put("parametersMap", parametersMap);


        WordTemplateUtils.getWord("C:\\ZhaoYangyang\\new doc.docx","C:\\ZhaoYangyang\\new doc2.docx",wordDataMap);




    }

}

然後,就會看到,生成的文檔。當然你也可以把它轉換成byte[],傳到瀏覽器下載。

就是這樣。歡迎大家交流技術 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章