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为例
配置成功后的图片
2.1 设置域文字及图片展示
WPS文档页面,点击插入,文档部件;再选择域如上图所示,会弹出如下图片:
以上步骤,就实现自定义域的占位符;其他不特别说明,惯例一样的;主要是表格,顾名思义就是表格每一行的数据都存在相同的占位符;且后台必须是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 效果展示
4 异常问题描述
输入输出流,常见的异常:读取文件占用、解析替换文件有换行时会报:“null”;
常见的读取文件路径不正确导出替换失败的情况,需要用到的小伙伴们,需要多注意观察。