基于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){//遍历获取段落中所有的runsList<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设为nullStringBuilder 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[],传到浏览器下载。
就是这样。欢迎大家交流技术
基于POI的wod模板文件,导入参数,导出最终文件相关推荐
- Excel文件导入,导出
前端Excel文件导入,导出 Excel文件导入,导出 功能快捷键 文件导入 文件解析 如何改变文本的样式 exportExcel.js 生成一个适合你的列表 创建一个表格 设定内容居中.居左.居右 ...
- springboot使用jxls导出excel___(万能通用模板)--- SpringBoot导入、导出Excel文件___SpringBoot整合EasyExcel模板导出Excel
springboot使用jxls导出excel 实现思路: 首先在springBoot(或者SpringCloud)项目的默认templates目录放入提前定义好的Excel模板,然后在具体的导出接口 ...
- (万能通用模板)--- SpringBoot导入、导出Excel文件
先把项目的demo发一下,看完文章可以看一下,demo 前言:最近做项目过程中使用到了一个权限管理框架:若依,使用过程中发现他的文件导入和导出功能非常的实用,在这里特此做一个小demo跟大家分享一下. ...
- php inputcsv,php实现CSV文件导入和导出
这篇文章主要介绍了php实现CSV文件导入和导出的方法,具有一定的参考价值,需要的朋友可以参考下 项目开发中,很多时候要将外部CSV文件导入到数据库中或者将数据导出为CSV文件,那么具体该如何实现呢? ...
- h5页面如何预览excel文件_如何使用JavaScript实现前端导入和导出excel文件?(H5编辑器实战复盘)...
前言 最近笔者终于把H5-Dooring的后台管理系统初步搭建完成, 有了初步的数据采集和数据分析能力, 接下来我们就复盘一下其中涉及的几个知识点,并一一阐述其在Dooring H5可视化编辑器中的解 ...
- 如何使用JavaScript实现前端导入和导出excel文件(H5编辑器实战复盘)
前言 最近笔者终于把H5-Dooring的后台管理系统初步搭建完成, 有了初步的数据采集和数据分析能力, 接下来我们就复盘一下其中涉及的几个知识点,并一一阐述其在Dooring H5可视化编辑器中的解 ...
- 从零开始学R(四)——常用命令:帮助,文件导入与导出与基本操作
R的常用命令 R的命令非常多,但是有一些命令是非常常用的,在这里我做一个汇总,便于大家使用.(Tips:关于下载和安装R的package以及工作目录的获得与设置命令在第二篇已经聊过,此时就不再赘述了, ...
- 如何使用JavaScript导入和导出Excel文件
本文由葡萄城技术团队于原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. JavaScript是一个涵盖多种框架.直译式.可以轻松自定义客户端的脚本 ...
- java mvc 导入_Java SpringMVC文件导入和导出
J示例代码: @ResponseBody @RequestMapping(value = "/fileUpload", method = RequestMethod.POST) p ...
最新文章
- 那些方式可以合并php数组,合并数组(PHP)
- 通过正则表达式分句提取中文内容
- linux日志自动按天保存,linux实现按天生成日志文件并自动清理
- Fiddler使用教程(收藏)
- 阿里钉钉向1000万企业组织免费开放在家办公系统
- fiddle下载安装教程
- Qt制作音乐播放器按钮
- 如何将mac中的资料拷贝到U盘,移动硬盘(实用!!!)
- 实体书回暖?码洋过10,000,000的随想
- 新版本微信PC端小程序打不开 一直加载 白屏等问题及解决方案
- Qt QImage scaled方法缩放中的问题
- MySql查询本周或下周过生日的人
- 上海浦东新区计算机学校排名2015,2015年上海市浦东新区初级中学最新排名.pdf
- isin用计算机怎么打,教你如何利用hackintool给你的电脑打缓冲帧
- ad15原理图中变压器种类_最简单的变压电路图大全(十一款最简单的变压电路设计原理图详解)...
- 笔记本重装后回收站文件怎么恢复
- 吸塑包装盒是怎么被制作生产出来的
- 【论文阅读】Dimensionality Reduction by Learning an Invariant Mapping
- puppet3d_发挥作用:Puppet3D如何使您的游戏栩栩如生
- pyscript的简单应用