基于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[],传到浏览器下载。

就是这样。欢迎大家交流技术 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值