当前位置: 首页 > article >正文

Java后端使用XWPFDocument生成word文档,踩坑

以下都是借鉴网上内容:
环境 纯后端, java, spring项目 maven管理.

maven内容:

		<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.16</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.16</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.3</version>
        </dependency>

运行时候如果报错: XmlOptions.setEntityExpansionLimit ( ) 方法找不到,那就是xmlbeans版本太低 升级以下:
就可以解决找不到方法:问题 如果没报错 ( 就把改依赖忽略),

 	<dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>3.1.0</version>
        </dependency>

代码如下 工具类一 :

package com.bsoft.hesp.utils.word;

import java.util.*;

import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;

public class WordExportUtil {
    private WordExportUtil() {
    }

    /**
     * 替换文档中段落文本
     *
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     */
    public static void changeParagraphText(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) {
                    //替换模板原来位置
                    String textValue = changeValue(run.toString(), textMap);
                    if (run.toString().toLowerCase().indexOf("checkbox_") != -1) {// 复选框填充值
                        run.setFontFamily("SimSun");
                        String[] tArr = textValue.split("@@@");
                        for (int i = 0; i < tArr.length; i++) {
                            if (i == 0) {
                                run.setText(tArr[i], 0);
                            } else {
                                run.setText(tArr[i]);
                            }
                            if (i != tArr.length - 1) {
                                run.addBreak();//换行
                            }
                        }
                    } else {
                        run.setText(textValue, 0);
                    }
                }
            }
        }
    }

    /**
     * 复制表头 插入行数据,这里样式和表头一样
     *
     * @param document    docx解析对象
     * @param tableList   需要插入数据集合
     *                    tableIndex表格索引,在word表格对象集合中是第几个表格,从0开始
     * @param headerIndex 表头的行索引,从0开始
     */
    public static void copyHeaderInsertText(XWPFDocument document, List<Map<String, String>> tableList, String[] fields, int tableindex, int headerIndex) {
        if (null == tableList || null == fields) {
            return;
        }
        //获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        XWPFTableRow copyRow = tables.get(tableindex).getRow(headerIndex);
        List<XWPFTableCell> cellList = copyRow.getTableCells();
        if (null == cellList) {
            return;
        }
        //遍历要添加的数据的list
        for (int i = 0; i < tableList.size(); i++) {
            //插入一行
            XWPFTableRow targetRow = tables.get(tableindex).insertNewTableRow(headerIndex + i);
            //复制行属性
            targetRow.getCtRow().setTrPr(copyRow.getCtRow().getTrPr());

            Map<String, String> map = tableList.get(i);

            // 字段匹配后,插入单元格并赋值
            for (String field : fields) {
                int idx = 0;

                for (Map.Entry<String, String> entry : map.entrySet()) {
                    if (!field.equals(entry.getKey())) continue;

                    XWPFTableCell sourceCell = cellList.get(idx);
                    //插入一个单元格
                    XWPFTableCell targetCell = targetRow.addNewTableCell();
                    //复制列属性
                    targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());


                    //targetCell.setText(entry.getValue());
                    /** XWPFTableCell 无法直接设置字体样式,故换一种方式 **/
                    //获取 XWPFTableCell 的CTTc
                    CTTc ctTc = targetCell.getCTTc();
                    //获取 CTP
                    CTP ctP = (ctTc.sizeOfPArray() == 0) ? ctTc.addNewP() : ctTc.getPArray(0);
                    //getParagraph(ctP) 获取 XWPFParagraph
                    XWPFParagraph par = targetCell.getParagraph(ctP);
                    //XWPFRun   设置格式
                    XWPFRun run = par.createRun();

                    String textValue = entry.getValue();
                    // 复选框填充值
                    if (field.toLowerCase().indexOf("checkbox_") != -1) {
                        run.setFontFamily("SimSun");
                        String[] tArr = textValue.split("@@@");
                        for (int j = 0; j < tArr.length; j++) {
                            if (j == 0) {
                                run.setText(tArr[j], 0);
                            } else {
                                run.setText(tArr[j]);
                            }
                            if (j != tArr.length - 1) {
                                run.addBreak();//换行
                            }
                        }
                    } else {
                        run.setText(textValue, 0);
                    }

                    idx++;
                }
                ;
            }
        }
        if (tableList.size() > 0) {
            List<XWPFTableRow> rows = tables.get(tableindex).getRows();
            int rowLength = rows.size();
            deleteTable(tables.get(tableindex), tableList.size() + headerIndex, rowLength);
        }
    }

    /**
     * 删除表格行
     *
     * @param table     表格对象
     * @param fromIndex 从第几行,从0开始
     * @param toIndex   到第几行,从0开始
     */
    public static void deleteTable(XWPFTable table, int fromIndex, int toIndex) {
        for (int i = fromIndex; i < toIndex; i++) {
            table.removeRow(fromIndex);
        }
    }

    /**
     * 替换表格对象方法
     *
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     */
    public static void changeTableText(XWPFDocument document, Map<String, String> textMap) {
        //获取表格对象集合
        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();
                    //遍历表格,并替换模板
                    eachTable(rows, textMap);
                }
            }*/

            //判断表格是需要替换还是需要插入,判断逻辑有$为替换
            if (checkText(table.getText())) {
                List<XWPFTableRow> rows = table.getRows();
                //遍历表格,并替换模板
                eachTable(rows, textMap);
            }
        }
    }

    /**
     * 遍历表格,并替换模板
     *
     * @param rows    表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(List<XWPFTableRow> rows, Map<String, String> textMap) {
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                //判断单元格是否需要替换
                if (checkText(cell.getText())) {
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
                            fillCheckBox(run, textMap);
                        }
                    }
                }
            }
        }
    }

    public static void fillCheckBox(XWPFRun run, Map<String, String> textMap) {
        String textValue = changeValue(run.toString(), textMap);
        if (run.toString().toLowerCase().indexOf("checkbox_") != -1) {// 复选框填充值
            run.setFontFamily("SimSun");
            String[] tArr = textValue.split("@@@");
            for (int i = 0; i < tArr.length; i++) {
                if (i == 0) {
                    run.setText(tArr[i], 0);
                } else {
                    run.setText(tArr[i]);
                }
                /*if (i != tArr.length - 1) {
                                        run.addBreak();//换行
                   }*/
            }
        } else {
            run.setText(textValue, 0);
        }
    }


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

    /**
     * 判断文本中是否包含$
     *
     * @param text 文本
     * @return 包含返回true, 不包含返回false
     */
    public static boolean checkText(String text) {
        boolean check = false;
        if (text.indexOf("$") != -1) {
            check = true;
        }
        return check;
    }
}

package com.bsoft.hesp.utils.word;

import org.apache.http.HttpHeaders;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

/**
 * Author: 
 * Date: 2023/11/27 15:05
 **/
public class WordGenerate {
    /**
     * word生成
     * @throws FileNotFoundException
     */
    public static void generateWord(List<Map<String, String>> list, Map<String, String> fileAddress, HttpServletResponse response) throws FileNotFoundException {
        XWPFDocument document = null;
        try {
            for (Map<String, String> tableMap : list) {
                String classpath = WordGenerate.class.getResource("").getPath();
                File f = new File(classpath+"/模板20231124.docx");
                FileInputStream fileInputStream = new FileInputStream(new File(f.getPath()));
                FileOutputStream fileOutStream = new FileOutputStream("E:\\" + tableMap.get("fileName") + ".docx");
                try {
                    /**解析docx模板并获取document对象**/
                    document = new XWPFDocument(fileInputStream);
                    /**替换docx table中文本字段**/
                    WordExportUtil.changeTableText(document, tableMap);
                    document.write(fileOutStream);

                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (document != null) {
                        try {
                            document.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (null != fileInputStream) {
                        try {
                            fileInputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (null != fileOutStream) {
                        try {
                            fileOutStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


http://www.kler.cn/a/147802.html

相关文章:

  • [Linux网络编程]10-http协议,分别使用epoll和libevent两种方式实现B/S服务器
  • MySQL查询某个数据库中特定表的空间占用大小
  • Python中的HTML
  • 使用CNN进行验证码识别:深度学习与图像预处理教程
  • uni-app之数据驱动的picker选择器( uni-data-picker)之可以选择到任意级别
  • 力扣-Mysql-3308- 寻找表现最佳的司机(中等)
  • 【心得】XXE漏洞利用个人笔记
  • Python3.6.8升级Python3.12.0版本小记
  • Xshell远程登录AWS EC2 Linux实例
  • Linux—进程状态、僵尸进程、孤独进程、优先级
  • 【攻防世界-misc】reverseMe
  • LFM信号分析
  • 入侵redis之准备---Linux关于定时任务crontab相关知识了解配合理解shell反弹远程控制
  • 淘宝API接口系列:连接商户与消费者的桥梁
  • 【刷题笔记】分糖果||数组||暴力通过||符合思维方式||多案例分析
  • 饰品价格持续下跌,steam搬砖还有搞头吗?
  • 智能优化算法应用:基于蜻蜓算法无线传感器网络(WSN)覆盖优化 - 附代码
  • 锐捷:下一代防火墙修改密码
  • 【Qt】QStackedWidget、QRadioButton、QPushButton及布局实现程序首页自动展示功能
  • Android中根据字符串动态获取资源文件ID
  • 食品行业研发知识管理:企业网盘的选择与优势
  • 人民币已初步具备了国际使用的网络效应/首批疏解的在京部委所属4所高校雄安校区开工建设/墨茉点心局撤出北京市场
  • python读取PDF文件中的指定页码的范围并存储到指定的文件名
  • Rust语言入门教程(七) - 所有权系统
  • 阅读笔记——《Removing RLHF Protections in GPT-4 via Fine-Tuning》
  • C# 实现微信退款及对帐