springBoot使用poi-tl生成word(生成文字,表格和段落等内容)

前言

poi-tl(poi template language) 是word的模板引擎,使用poi-tl生成的word文档中生成表格、段落、图片等 都可以。 在文档的任何地方做任何事情(Do Anything Anywhere)是poi-tl的星辰大海。

  • 创建文本的基本概念
    • XWPFDocument代表一个docx文档,其可以用来读docx文档,也可以用来写docx文档
    • XWPFParagraph代表文档、表格、标题等种的段落,由多个XWPFRun组成
    • XWPFRun代表具有同样风格的一段文本
    • XWPFTable代表一个表格
    • XWPFTableRow代表表格的一行
    • XWPFTableCell代表表格的一个单元格

模板--word模板

  • 这是我创建.docx 文件的模板,把它作为模板将数据依次填写到对应的表格中

image.png

  • 导出之后的模板如下图,接下来我将安装我的模板的内容依次说明文件是怎么生成的,数据是怎么写在模板中的

image.png

段落的生成

段落的生成可以使用俩中方法来实现,一种是有匹配的内容,另一种是没有匹配的内容,但是在开头有一个标志

1.${}包裹

image.png

这种的核心思想就是获取段落中的文字是否被${}包裹着,如果被包裹,则替换当前的文字

/**
 * 匹配传入信息集合与模板
 * @param value 模板需要替换的区域
 * @param textMap 传入信息集合
 * @return 模板需要替换区域信息集合对应值
 */
public static String replaceWordContent(String value, Map<String, String> textMap){
    Set<Entry<String, String>> textSets = textMap.entrySet();
    for (Entry<String, String> textSet : textSets) {
        //匹配模板与替换值 格式${key}
        String key = "${"+textSet.getKey()+"}";
        if(value.contains(key)){
            value = textSet.getValue();
        }
    }
    //模板未匹配到区域替换为空
    if(checkText(value)){
        value = "";
    }
    return value;
}
复制代码

2.开头有一个标志

开头有一个标志 说的是在段落的开头需要有一个标志,作为当检索到这个标志的时候,开始在文档上写入内容,如下图中的开始就作为一个标志

image.png


private void iteratorParagraph(Map<String,Object> params,XWPFDocument document) throws  IOException{
        // 创建段落
        XWPFParagraph graph = null;
        // 在文档的基础上创建一个段落
        graph = document.createParagraph();
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for(int i =0;i<paragraphs.size();i++){
            // 获取每一个段落的内容
            XWPFParagraph graph1= paragraphs.get(i);
                //这里的“开始”就是一个标志
            if(graph1.getText().equals("开始")){
                // 在一个段落中新加一行
                XWPFRun row1 = graph.createRun();
                //addCarriageReturn()换行,row.addBreak()——这个也是换行
                row1.addCarriageReturn();
                // 省略很多内容.......
                String str = "";
                for (String k : params.keySet()){
                    if(k == "description"){
                        Object obj = params.get(k);
                        str = obj.toString();
                        break;
                    }
                }
                StringBuilder stringBuilder = new StringBuilder();
                for(int j = 0; j< 64-str.length();j++){
                    // 当后面不足时,添加空格内容
                    stringBuilder.append("");
                }
                document.removeBodyElement(document.getPosOfParagraph(graph1));
            }
        }
    }
复制代码

表格的内容

表格中${}

当模板中有${}包裹的时候,使用Map<String,String> textMap来存储键值对,当key == 模板表格中的值,便将Value 写在当前的单元格中。

/**
 * 遍历表格
 * @param rows 表格行对象
 * @param textMap 需要替换的信息集合
 */
private static void replaceWordTableContent(List<XWPFTableRow> rows ,Map<String, String> textMap){
    for (XWPFTableRow row : rows) {
        List<XWPFTableCell> cells = row.getTableCells();
        for (XWPFTableCell cell : cells) {
            //判断单元格是否需要替换
            // checkText()判断表格模板中是否被${}包裹
            if(checkText(cell.getText())){
                List<XWPFParagraph> paragraphs = cell.getParagraphs();
                for (XWPFParagraph paragraph : paragraphs) {
                    List<XWPFRun> runs = paragraph.getRuns();
                    for (XWPFRun run : runs) {
                        //replaceWordContent() 遍历Map,依次取出Map 中的key和value
                        run.setText(replaceWordContent(run.toString(), textMap),0);
                    }
                }
            }
        }
    }
}
复制代码

表格中没有${}

当表格中没有${}包裹,获取到空的单元格,使用遍历行和列,获取到单元格cell,同时调用cell.setText()的方法,依次将数据写入到对应的单元格中。

    // 遍历文档中的表格
    private void iteratorTable(Map<String,Object> params,XWPFDocument document) throws IOException{
        List<XWPFTableRow> rows = null;
        List<XWPFTableCell> cells = null;
        // 获取文档中的表格内容
        List<XWPFTable> tables = document.getTables();
        for(XWPFTable table : tables){
            rows = table.getRows();
            // 遍历表格中的所有的行
            for(XWPFTableRow row : rows){
                cells = row.getTableCells();
                // 遍历这一行的单元格
                for(XWPFTableCell cell : cells){
                    // 判断改单元格的内容是否是字符串字段
                    if(strMatcher(cell.getText()).find()){
                        //replaceInStr()替换字符串,可以多行也可以一行
                        replaceInStr(cell,params,document);
                        continue;
                    }
                }
            }
        }
    }
复制代码
  • 动态生成表格行数

这是我在上班的时候遇到的一个情景,一起记录子这里: 获取到一个动态的list 数据,就是当行和列不固定时,需要根据实际需求来动态生成行的内容,核心思路就是判断当前list(List<String[]> tableList)的长度来动态增加行,

/**
   * 为表格插入数据,行数不够添加新行
   * @param table 需要插入数据的表格
   * @param tableList 插入数据集合
*/

private static void insertTable(XWPFTable table, List<String[]> tableList){
  //创建行,根据需要插入的数据添加新行,不处理表头
  for(int i = 1; i < tableList.size(); i++){
      XWPFTableRow row =table.createRow();
     }
  //遍历表格插入数据
  List<XWPFTableRow> rows = table.getRows();
  for(int i = 1; i < rows.size(); 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]);
       }
  }
}
复制代码
  • 跨行合并

image.png

当数据传递的数据为空时,需要显示一个暂无参数,同时合并单元格

/**
 * 合并列
 * @param table
 * @param rowNumber 需要合并的列在的行
 * @param fromCol 开始列
 * @param toCol 结束列
 */
private static void mergeColumns(XWPFTable table, int rowNumber, int fromCol, int toCol) {
    XWPFTableRow row = table.getRow(rowNumber);
    for(int index = fromCol; index < toCol; index++){
        CTHMerge hMerge = CTHMerge.Factory.newInstance();
        if(index == fromCol){
            hMerge.setVal(STMerge.RESTART);
        } else {
            hMerge.setVal(STMerge.CONTINUE);
        }
        XWPFTableCell cell = row.getCell(index);
        CTTcPr tcPr = cell.getCTTc().getTcPr();
        if (tcPr != null) {
            tcPr.setHMerge(hMerge);
        } else {
            tcPr = CTTcPr.Factory.newInstance();
            tcPr.setHMerge(hMerge);
            cell.getCTTc().setTcPr(tcPr);
        }
    }
}
复制代码
  • 跨列合并

image.png

/**
 * 当接口无参数时,合并行
 * @param table 当前表
 * @param col 需要合并的行
 * @param fromRow 开始行
 * @param toRow 结束行
 * */
private static void  mergeRow(XWPFTable table, int col, int fromRow, int toRow){
    //index 为 行索引
    for (int index = fromRow; index <= toRow ; index++) {
        // 重新创建类加载---类似于new()
        CTVMerge merge = CTVMerge.Factory.newInstance();
        if(index == fromRow){
            merge.setVal(STMerge.RESTART);
        }else{
            merge.setVal(STMerge.CONTINUE);
        }
        XWPFTableCell tableCell = table.getRow(index).getCell(col);
        CTTcPr tcPr = tableCell.getCTTc().getTcPr();
        if(tcPr != null){
            tcPr.setVMerge(merge);
        }else{
            tcPr = CTTcPr.Factory.newInstance();
            tcPr.setVMerge(merge);
            tableCell.getCTTc().setTcPr(tcPr);
        }
    }
}
复制代码

补充

设置字体和字号

List<XWPFParagraph> graphs = cell.getParagraphs();
for(XWPFParagraph graph : graphs){
   XWPFRun run = graph.createRun();
    run.setFontSize(12);
    run.setFontFamily("宋体");
    run.setText(tableList.get(i-1)[j]);
}
复制代码

设置表格中的内容的水平和垂直对齐方式

// 输入的内容垂直居中
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
// 设置水平居中
CTTc cttc = cell.getCTTc();
CTTcPr ctPr = cttc.addNewTcPr();
ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
复制代码

最后

这篇文章是我看了网上好多博主的文章之后,自己测试之后摸索总结出来,希望对你也有帮助。留下官方文档,值得看看。

这篇文章中用到的maven依赖有:

<dependency>
   <groupId>com.deepoove</groupId>
   <artifactId>poi-tl</artifactId>
   <version>1.10.0</version>
</dependency>
<dependency>
   <groupId>org.apache.poi</groupId>
   <artifactId>ooxml-schemas</artifactId>
   <version>1.3</version>
</dependency>
复制代码

实现的功能的所有代码

// 导入的相关的包文件
import com.szht.mapper.project.InterfaceMapper;
import com.szht.model.entity.project.InterfaceEntity;
import com.szht.model.entity.project.InterfaceWordEntity;
import com.szht.service.project.ExportWordInterfaceService;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
复制代码
 /** 根据interfaceEntity,重写导出word的方法*/
    @Override
    public void exportWord(InterfaceEntity interfaceEntity, HttpServletResponse response)throws  IOException{
        //查询业务参数的内容
        List<InterfaceWordEntity> interfaceWordEntity = getWordInterfaceById(interfaceEntity.getId());
        String path = new File("src\\main\\resources\\templates\\temp.docx").getAbsolutePath();
        //导出的名字
        String fileName = interfaceEntity.getName() +".docx";
        // 在文档中的段落写入文字
        Map<String,String> textMap=new HashMap<>();
        textMap.put("name",interfaceEntity.getName());
        textMap.put("fullPath",interfaceEntity.getFullPath());
        textMap.put("updateTime",interfaceEntity.getUpdateTime());
       
        if(interfaceEntity.getDescription() != null){
            textMap.put("description",interfaceEntity.getDescription());
        }

        List<String[]> tableList = new ArrayList<>();
        // 当数据集参数大于0时们将数据依次,不为空
        if(interfaceWordEntity.size() > 0){
            for(InterfaceWordEntity paramEntity : interfaceWordEntity) {
                tableList.add(new String[]{paramEntity.getInterfaceId(),paramEntity.getRequiredParameter(),paramEntity.getParamType(),paramEntity.getRequired().toString(),paramEntity.getParamTips()});
            }
        }else{
            // 当数据集参数大于等于0,参数为空的时候,将导出的参数列表合并单元格并且显示”暂无参数“
            tableList.add(new String[]{"暂无参数","","","",""});

        }
        // 给模板中的表格生成默认的数据内容
        List<String[]> defaultTableList = new ArrayList<>();
        defaultTableList.add(new String[]{"code","String","是","200:查询成功,其他:异常"});
        defaultTableList.add(new String[]{"message","String","是","返回信息"});
        defaultTableList.add(new String[]{"total","String","是","数据件数"});
        defaultTableList.add(new String[]{"data","List<Map<String, String>>","是","返回数据"});

        // 表格中的数据使用params,段落中的内容使用 testMap
        exportWordTemp(path,textMap,tableList,defaultTableList ,fileName,response);
    }
复制代码
// 使用Stream 读取和写入文件内容
    public static void  exportWordTemp(String inputPath, Map<String, String> textMap, List<String[]> tableList,List<String[]> defaultTableList,String fileName,HttpServletResponse response) {
        try {
            InputStream inputStream = new FileInputStream(inputPath);
            //获取文档XX.docx
            XWPFDocument document = new XWPFDocument(inputStream);
            // 替换文本对象
            replaceText(document,textMap);
            // 替换表格内容
            replaceTable(document,textMap,tableList,defaultTableList);
            OutputStream outputStream = response.getOutputStream();
            // 设置导出的内容是doc
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Content-Disposition", "attachment; fileName=" + fileName);
            // 将文件写入关闭流
            document.write(outputStream);
            outputStream.flush();
            document.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
复制代码
    /**
     *替换段落文本
     * @param document docx解析对象
     * @param textMap 需要替换的信息集合
     * */
    private static void replaceText(XWPFDocument document,Map<String,String> textMap){
        // 获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        // 取每一个段落
        for(XWPFParagraph paragraph :paragraphs){
            // 判断段落是否需要替换
            String text = paragraph.getText();
            if(checkText(text)){
                List<XWPFRun> runs = paragraph.getRuns();
                for(XWPFRun run : runs){
                    // 替换模板中的内容
                    run.setText(replaceWordContent(run.toString(),textMap),0);
                }
            }
        }
    }
复制代码
    /**
     * 判断文本中时候包含$
     * @param text 文本
     * @return 包含返回true,不包含返回false
     */
public static boolean checkText(String text){
        return text.contains("$");
    }
复制代码
    /**
     * 匹配传入信息集合与模板
     * @param value 模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    public static String replaceWordContent(String value, Map<String, String> textMap){
        Set<Entry<String, String>> textSets = textMap.entrySet();
        for (Entry<String, String> textSet : textSets) {
            //匹配模板与替换值 格式${key}
            String key = "${"+textSet.getKey()+"}";
            if(value.contains(key)){
                value = textSet.getValue();
            }
        }
        //模板未匹配到区域替换为空
        if(checkText(value)){
            value = "";
        }
        return value;
    }
复制代码
/**
     * 替换表格中的内容
     *@param document docx解析对象
     *@param textMap 需要替换的信息集合
     *@param tableList 需要插入的表格信息集合
     * */
    private static void replaceTable(XWPFDocument document,Map<String,String> textMap,List<String[]> tableList,List<String[]> defaultTableList){
        // 获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        for(int i = 0;i < tables.size();i++){
            // 只处理行数大于等于2的表格,且不处理表头
            XWPFTable table = tables.get(i);
            if(table.getRows().size() > 1){
                // 当表格中有$替换数据,没有则插入数据
                if(checkText(table.getText())){
                    List<XWPFTableRow> rows = table.getRows();
                    // 遍历表格,并替换模板
                    replaceWordTableContent(rows,textMap);
                }else {
                    // 当表格大于模板的行数时,新增行数
                    // tables.get(2) == table   <=> i ==2
                    if(i == 2){
                        // 当时第三张表的数据时,则添加默认数据
                        insertTable(tables,table,defaultTableList);
                    }else if(i == 1 && tableList.get(0)[0] == "暂无参数"){
                        for (int j = 0; j < tableList.size(); j++) {
                            mergeColumns(table,j+1,0,5);
                        }
                        insertTable(tables,table,tableList);
                    }else {
                        insertTable(tables,table,tableList);
                    }
                }
            }
        }
    }
复制代码
/**
     * 遍历表格
     * @param rows 表格行对象
     * @param textMap 需要替换的信息集合
     */
    private static void replaceWordTableContent(List<XWPFTableRow> rows ,Map<String, String> textMap){
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                //设置替换的表格中的内容垂直居中
                cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
                //设置替换的表格中的内容水平居中
                CTTcPr ctPr = cell.getCTTc().addNewTcPr();
                ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                cell.getCTTc().getPList().get(0).addNewPPr().addNewJc().setVal(STJc.LEFT);
                //判断单元格是否需要替换
                if(checkText(cell.getText())){
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
                            run.setText(replaceWordContent(run.toString(), textMap),0);
                            paragraph.setVerticalAlignment(TextAlignment.CENTER);
                            run.setFontFamily("宋体");
                            run.setFontSize(12);
                        }
                    }
                }
            }
        }
    }

复制代码
/**
     * 为表格插入数据,行数不够添加新行
     * @param tables 获取模板中的所有表格,取出其中的一张表
     * @param table 需要插入数据的表格
     * @param tableList 插入数据集合
     */
    private static void insertTable(List<XWPFTable> tables,XWPFTable table, List<String[]> tableList){
        //创建行,根据需要插入的数据添加新行,不处理表头
        for(int i = 1; i < tableList.size(); i++){
            XWPFTableRow row =table.createRow();
            //对于多个参数的表格,将interfaceID 跨行合并单元格,只对第一张表进行跨单元格合并
            if(tables.get(1) == table){
                mergeRow(table,0,i,i+1);
            }
        }
        //遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();
        for(int i = 1; i < rows.size(); i++){
            //为新增的表格设置行高 567 为 1cm
            table.getRows().get(i).setHeight(567);
            // 设置列宽
            // 表格属性
            CTTblPr tblPr = table.getCTTbl().addNewTblPr();
            //表格宽度
            CTTblWidth width = tblPr.addNewTblW();
            // 1710 :大约为3cm
            width.setW(BigInteger.valueOf(1710));
            //设置表格宽度为非自动
            //  width.setType(STTblWidth.DXA);
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for(int j = 0; j < cells.size(); j++){
                XWPFTableCell cell = cells.get(j);
                // 输入的内容垂直居中
                cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
                // 设置水平居中
                CTTc cttc = cell.getCTTc();
                CTTcPr ctPr = cttc.addNewTcPr();
                ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
                cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
                List<XWPFParagraph> graphs = cell.getParagraphs();
                for(XWPFParagraph graph : graphs){
                   XWPFRun run = graph.createRun();
                    run.setFontSize(12);
                    run.setFontFamily("宋体");
                    run.setText(tableList.get(i-1)[j]);
                }
            }
        }
    }
复制代码
 /**
     * 当接口无参数时,合并行
     * @param table 当前表
     * @param col 需要合并的行
     * @param fromRow 开始行
     * @param toRow 结束行
     * */
    private static void  mergeRow(XWPFTable table, int col, int fromRow, int toRow){
        //index 为 行索引
        for (int index = fromRow; index <= toRow ; index++) {
            // 重新创建类加载---类似于new()
            CTVMerge merge = CTVMerge.Factory.newInstance();
            if(index == fromRow){
                merge.setVal(STMerge.RESTART);
            }else{
                merge.setVal(STMerge.CONTINUE);
            }
            XWPFTableCell tableCell = table.getRow(index).getCell(col);
            CTTcPr tcPr = tableCell.getCTTc().getTcPr();
            if(tcPr != null){
                tcPr.setVMerge(merge);
            }else{
                tcPr = CTTcPr.Factory.newInstance();
                tcPr.setVMerge(merge);
                tableCell.getCTTc().setTcPr(tcPr);
            }
        }
    }
复制代码
 /**
     * 合并列
     * @param table
     * @param rowNumber 需要合并的列在的行
     * @param fromCol 开始列
     * @param toCol 结束列
     */
    private static void mergeColumns(XWPFTable table, int rowNumber, int fromCol, int toCol) {
        XWPFTableRow row = table.getRow(rowNumber);
        for(int index = fromCol; index < toCol; index++){
            CTHMerge hMerge = CTHMerge.Factory.newInstance();
            if(index == fromCol){
                hMerge.setVal(STMerge.RESTART);
            } else {
                hMerge.setVal(STMerge.CONTINUE);
            }
            XWPFTableCell cell = row.getCell(index);
            CTTcPr tcPr = cell.getCTTc().getTcPr();
            if (tcPr != null) {
                tcPr.setHMerge(hMerge);
            } else {
                tcPr = CTTcPr.Factory.newInstance();
                tcPr.setHMerge(hMerge);
                cell.getCTTc().setTcPr(tcPr);
            }
        }
    }
复制代码

猜你喜欢

转载自juejin.im/post/7039954144668418056