java+poi导出word文档,替换占位符

  • 实现步骤目录
  • 1.maven 导入依赖坐标
  • 2.设置邮件合并域(占位符)以WPS为例
  • 2.1 设置域文字及图片展示
  • 3. 工具类,本地测试,浏览器导出下载展示
  • 3.1 main方法代码实现
  • 3.2 浏览器导出实现
  • 3.3 效果展示
  • 4 异常问题描述


实现步骤目录

1.maven 导入依赖坐标

<dependency>
		<groupId>org.apache.poi</groupId>
		<artifactId>poi</artifactId>
		<version>3.14</version>
	</dependency>
	<dependency>
		<groupId>org.apache.poi</groupId>
		<artifactId>poi-ooxml</artifactId>
		<version>3.14</version>
	</dependency>
	<dependency>
		<groupId>org.apache.poi</groupId>
		<artifactId>poi-ooxml-schemas</artifactId>
		<version>3.14</version>
	</dependency>
	<dependency>
		<groupId>org.apache.xmlbeans</groupId>
		<artifactId>xmlbeans</artifactId>
		<version>2.6.0</version>
	</dependency>

2.设置邮件合并域(占位符)以WPS为例

配置成功后的图片

java 占位符 怎么替换 java替换word占位符_java 占位符 怎么替换

2.1 设置域文字及图片展示

java 占位符 怎么替换 java替换word占位符_开发语言_02

WPS文档页面,点击插入,文档部件;再选择域如上图所示,会弹出如下图片:

java 占位符 怎么替换 java替换word占位符_intellij-idea_03


以上步骤,就实现自定义域的占位符;其他不特别说明,惯例一样的;主要是表格,顾名思义就是表格每一行的数据都存在相同的占位符;且后台必须是List<Map<String,Object>>类型。

3. 工具类,本地测试,浏览器导出下载展示

/**
 * 替换文档文档段落邮件合并(占位符)
 *
 * @param paragraphList
 * @param formData
 */
 public static void replaceForm(List paragraphList, Map<String, Object> formData) {
 for (XWPFParagraph par : paragraphList) {
 List runs = par.getRuns();//获取段落的文本对象
 for (XWPFRun run : runs) {
 String text = run.getText(0);//获取文本的域的名称
 if (text != null) {
 for (Map.Entry<String, Object> entry : formData.entrySet()) {//遍历formData
 String key = entry.getKey();
 Object value = entry.getValue();
 if (text.indexOf(key) != -1) {//获取formData的key值是否和域名称相同,相同则替换值
 if (value == null) {
 run.setText(“”, 0);
 } else {
 text = text.replace(key, value.toString());
 run.setText(text, 0);
 break;
 }
 }
 }
 }
 }
 }
 }
/**
 * 遍历表格数据,替换表格占位符
 *
 * @param tables  表格
 * @param listMap 列表数据,替换表格的数据
 */
public static void replaceTable(List<XWPFTable> tables, List<Map<String, Object>> listMap) {
    for (int tableCount = 1; tableCount < listMap.size(); tableCount++) { //复制添加listMap行数的值
        /*  这里也能复制行,但无法复制的内容无法替换text文本
        CTRow ctrow = CTRow.Factory.parse(tables.get(0).getRow(1).getCtRow().newInputStream());//重点行
        XWPFTableRow row = new XWPFTableRow(ctrow, tables.get(0));//创建相同样式行
        tables.get(0).addRow(row);
         */
        DocumentUtil.insertRow(tables.get(0), 1, tableCount + 1);
    }
    for (XWPFTable xwpfTable : tables) {
        //获取表格行数
        int count = xwpfTable.getNumberOfRows();
        //遍历行
        for (int i = 1; i < count; i++) {
            //获取表格的第i行数
            XWPFTableRow row = xwpfTable.getRow(i);
            //遍历的数据
            Map<String, Object> map = listMap.get(i - 1);
            //获取该行的所有列
            List<XWPFTableCell> cells = row.getTableCells();
            //遍历每一列
            for (int j = 0; j < cells.size(); j++) {
                //获取每一列的段落内容
                List<XWPFParagraph> paragraphs = cells.get(j).getParagraphs();
                //替换每个段落的占位符
                replaceForm(paragraphs, map);
            }
        }
    }
}
/**
 * 格式化参数,为参数加上前后缀
 *
 * @param formData 格式化map数据
 * @return
 */
public static Map<String, Object> dataFormat(Map<String, Object> formData) {
    Map<String, Object> map = new HashMap<>();
    for (Map.Entry<String, Object> e : formData.entrySet()) { //格式化参数
        map.put("«" + e.getKey() + "»", e.getValue());
    }
    return map;
}

/**
 * insertRow 在word表格中指定位置插入一行,并将某一行的样式复制到新增行
 *
 * @param copyRowLIne 需要复制的行
 * @param newRowLIne  需要新增一行的位置
 */
public static void insertRow(XWPFTable table, int copyRowLIne, int newRowLIne) {
    // 在表格中指定的位置新增一行
    XWPFTableRow targetRow = table.insertNewTableRow(newRowLIne);
    // 获取需要复制行对象
    XWPFTableRow copyRow = table.getRow(copyRowLIne);
    //复制行对象
    targetRow.getCtRow().setTrPr(copyRow.getCtRow().getTrPr());
    //需要复制的行的列
    List<XWPFTableCell> copyCells = copyRow.getTableCells();
    //复制列对象
    XWPFTableCell targetCell = null;
    for (int i = 0; i < copyCells.size(); i++) {
        XWPFTableCell copyCell = copyCells.get(i);
        targetCell = targetRow.addNewTableCell();
        targetCell.getCTTc().setTcPr(copyCell.getCTTc().getTcPr());
        if (copyCell.getParagraphs() != null && copyCell.getParagraphs().size() > 0) {
            targetCell.getParagraphs().get(0).getCTP().setPPr(copyCell.getParagraphs().get(0).getCTP().getPPr());
            if (copyCell.getParagraphs().get(0).getRuns() != null && copyCell.getParagraphs().get(0).getRuns().size() > 0) {
                List<XWPFRun> runs = copyCell.getParagraphs().get(0).getRuns();
                String text = "";
                for (XWPFRun xwpfRun : runs) {
                    if (!IsEmptyUtil.isEmpty(xwpfRun.getText(0))) {
                        text = xwpfRun.getText(0);
                        break;
                    }
                }
                XWPFRun cellR = targetCell.getParagraphs().get(0).createRun();
                cellR.setBold(copyCell.getParagraphs().get(0).getRuns().get(0).isBold());
                //获取到复制行的文本,加复制到列的占位符
                cellR.setText(text);
            }
        }
    }
}

3.1 main方法代码实现

/**
 * 本地main方法,实现导出
 *
 * @param args
 */
 public static void main(String[] args) {
 String docPath = “D:\测试文档.docx”; //io读取路径
 Map<String, Object> formData = new HashMap<String, Object>(); //表单数据
 formData.put(“«userName»”, “张三”);
 formData.put(“«gender»”, “男”);
 formData.put(“«hobby»”, “喜欢篮球”);
 formData.put(“«address»”, “天桥底下”);
 List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();//表格列表数据
 Map<String, Object> map = new HashMap<String, Object>();
 map.put(“«index»”, “1”);
 map.put(“«number»”, “3次”);
 map.put(“«competition»”, “2020年世界篮球赛”);
 map.put(“«result»”, “险胜,没有FMVP”);
 map.put(“«remark»”, “要不是王五这个老六裁判,FMVP绝对是我的”);
 listMap.add(map);
 Map<String, Object> map1 = new HashMap<String, Object>();
 map1.put(“«index»”, “2”);
 map1.put(“«number»”, “2次”);
 map1.put(“«competition»”, “2021年世界杯篮球赛”);
 map1.put(“«result»”, “完胜,FMVP”);
 map1.put(“«remark»”, “李四,被我张三打自闭”);
 listMap.add(map1);
 Map<String, Object> map2 = new HashMap<String, Object>();
 map2.put(“«index»”, “3”);
 map2.put(“«number»”, “4次”);
 map2.put(“«competition»”, “2022年世界杯篮球赛”);
 map2.put(“«result»”, “完胜,FMVP,三连霸”);
 map2.put(“«remark»”, “李四,再次被我张三打自闭”);
 listMap.add(map2);
 try {
 //读取文件
 InputStream in = new FileInputStream(docPath);
 //加载文档
 XWPFDocument doc = new XWPFDocument(in);
 //段落标记字符替换
 List paragraphList = doc.getParagraphs();
 //获取所有表格标记字符替换
 List tables = doc.getTables();
 //替换段落占位符
 DocumentUtil.replaceForm(paragraphList,formData);
 //替换表格列表的占位符
 DocumentUtil.replaceTable(tables,listMap);
 // 输出文档
 doc.write(new FileOutputStream(“D:\测试文档1.docx”));
 doc.close();
 } catch (Exception e) { //导出word通常出现的报错,null ,找不到文件,读取不到文件较为多;因此异常捕获;避免html页面停顿或者未响应的不友好出现
 System.out.println(“导出word文档错误” + e.getMessage());
 }
 }

3.2 浏览器导出实现

/**
 * 导出word文档
 *
 * @param request http请求
 * @param response http响应
 */
 @RequestMapping(value = “/exportDocument”,method = RequestMethod.GET)
 public void exportDocument(HttpServletRequest request, HttpServletResponse response) {
 //为了便捷,手动造数据看结果;需要拿真实业务数据的小伙伴,可以根据前端id 查询得出 fromData,listMap,docPath即可
 String docPath = “D:\测试文档.docx”;
 Map<String, Object> formData = new HashMap<String, Object>(); //表单数据
 formData.put(“«userName»”, “张三”);
 formData.put(“«gender»”, “男”);
 formData.put(“«hobby»”, “喜欢篮球”);
 formData.put(“«address»”, “天桥底下”);
 List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();//表格列表数据
 Map<String, Object> map = new HashMap<String, Object>();
 map.put(“«index»”, “1”);
 map.put(“«number»”, “3次”);
 map.put(“«competition»”, “2020年世界篮球赛”);
 map.put(“«result»”, “险胜,没有FMVP”);
 map.put(“«remark»”, “要不是王五这个老六裁判,FMVP绝对是我的”);
 listMap.add(map);
 Map<String, Object> map1 = new HashMap<String, Object>();
 map1.put(“«index»”, “2”);
 map1.put(“«number»”, “2次”);
 map1.put(“«competition»”, “2021年世界杯篮球赛”);
 map1.put(“«result»”, “完胜,FMVP”);
 map1.put(“«remark»”, “李四,被我张三打自闭”);
 listMap.add(map1);
 Map<String, Object> map2 = new HashMap<String, Object>();
 map2.put(“«index»”, “3”);
 map2.put(“«number»”, “4次”);
 map2.put(“«competition»”, “2022年世界杯篮球赛”);
 map2.put(“«result»”, “完胜,FMVP,三连霸”);
 map2.put(“«remark»”, “李四,再次被我张三打自闭”);
 listMap.add(map2);
 OutputStream outputStream = null;
 try {
 //读取文件
 InputStream in = new FileInputStream(docPath);
 //加载文档
 XWPFDocument doc = new XWPFDocument(in);
 //段落标记字符替换
 List paragraphList = doc.getParagraphs();
 //获取所有表格标记字符替换
 List tables = doc.getTables();
 //替换段落占位符
 DocumentUtil.replaceForm(paragraphList,formData);
 //替换表格列表的占位符
 DocumentUtil.replaceTable(tables,listMap);
 String docName = “测试文档”;
 String fileName = new String((docName).getBytes(“GB2312”), “ISO-8859-1”) + “.docx”;
 outputStream = response.getOutputStream();
 response.reset();
 response.setHeader(“Content-disposition”, “attachment;fileName=” + fileName);
 response.setContentType(“application/x-msdownload”);
 doc.write(outputStream);
 outputStream.flush();
 outputStream.close();
 } catch (Exception e) {
 System.out.println(“导出word文档错误” + e.getMessage());
 }
 }

3.3 效果展示

java 占位符 怎么替换 java替换word占位符_java_04

4 异常问题描述

输入输出流,常见的异常:读取文件占用、解析替换文件有换行时会报:“null”;
常见的读取文件路径不正确导出替换失败的情况,需要用到的小伙伴们,需要多注意观察。