这个是之前在网上找到的帖子,自己稍微改动了点,时间过去多半年了,原来的地址忘记了;
注意:(${变量名})变量必须一次性打出,否则根据数据替换变量的时候解析不到变量( 变量:userName,使用程序解析模板后,它解析出来可能是user 和 Name 也可能是其他,),也可以复制到文本编辑器,再从文本编辑器粘到木板上,推介复制到文本编辑器,在粘贴到模板内;
模板:
依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
controller
@RequestMapping(value="/exportWordTest", method = RequestMethod.POST)
public ResponseEntity<byte[]> exportWordTest(@RequestBody Map<String,String> map) throws Exception{
String fileName = "3345455.docx";
HttpHeaders headers = new HttpHeaders();
//下载显示的文件名,并解决中文名称乱码问题
String downloadFileName = new String(fileName.getBytes(StandardCharsets.UTF_8.name()),StandardCharsets.ISO_8859_1.name());
//通知浏览器以attachment(下载方式)打开
headers.setContentDispositionFormData("attachment", downloadFileName);
//applicatin/octet-stream: 二进制流数据(最常见的文件下载)
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
byte[] byteArr = bomService.exportWord(map);
return new ResponseEntity<>(byteArr, headers, HttpStatus.CREATED);
}
service实现类
public byte[] exportWord(Map<String, String> map) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
XWPFDocument document = null;
try {
//解析docx模板并获取document对象
InputStream inputStream = FileUtil.getResourcesFileInputStream("template/TZY-15B过程记录卡.docx");
document = new XWPFDocument(inputStream);
//开始查询并组装数据;
Map<String, String> tableHeadMap = new HashMap<>();
List<String[]> familyList = new ArrayList<>();
//动态表格数据
//组装表头的参数;
tableHeadMap.put("version",bom.getBomVersion());//记录卡版次:生产bom中的版本号
//替换表格中不需要循环的参数
ExportWordUtil.changeTableText(document, tableHeadMap);
//动态表格循环数据填充
ExportWordUtil.copyHeaderInsertText(document, familyList , 4);
document.write(byteOut);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (document != null) {
try {
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return byteOut.toByteArray();
}
工具类ExportWordUtil
方法:changeTableText这块用的时候根据自己使用情况改造一下;
package com.goocidata.supplyx.mes.util;
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @ClassName ExportWordUtil
* @Description TODO
* @Date 2022/2/24 15:58
*/
public class ExportWordUtil {
private ExportWordUtil() {
}
/**
* 替换文档中段落文本
*
* @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 s = changeValue(run.toString(), textMap);
run.setText(s , 0);
}
}
}
}
/**
* 复制表头 插入行数据,这里样式和表头一样
* @param document docx解析对象
* @param tableList 需要插入数据集合
* @param headerIndex 表头的行索引,从0开始
*/
public static void copyHeaderInsertText(XWPFDocument document, List<String[]> tableList, int headerIndex){
if(null == tableList){
return;
}
//获取表格对象集合
List<XWPFTable> tables = document.getTables();
int n = 0;
for (XWPFTable table : tables) {
if(n > 0){
break;
}
n++;
XWPFTableRow copyRow = table.getRow(headerIndex);
List<XWPFTableCell> cellList = copyRow.getTableCells();
if (null == cellList) {
break;
}
//遍历要添加的数据的list
for (int i = 0; i < tableList.size(); i++) {
//插入一行
XWPFTableRow targetRow = table.insertNewTableRow(headerIndex + 1 + i);
//复制行属性
targetRow.getCtRow().setTrPr(copyRow.getCtRow().getTrPr());
String[] strings = tableList.get(i);
for (int j = 0; j < strings.length; j++) {
XWPFTableCell sourceCell = cellList.get(j);
//插入一个单元格
XWPFTableCell targetCell = targetRow.addNewTableCell();
//复制列属性
targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
targetCell.setText(strings[j]);
}
}
}
}
/**
* 替换表格对象方法
* @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);
}
}
}
}
/**
* 遍历表格,并替换模板
* @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) {
run.setText(changeValue(run.toString(), textMap), 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;
}
}