poi-tl
(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档 。
常用标签介绍(官网):
- 文本:{{var}} 2. 图片:{{@var}}
- 表格:{{#var}} 4. 列表:{{*var}}
- 区块对:{{?sections}}{{/sections}} 6. 嵌套:{{+var}}
本次制作的Demo,
简写代码、多注释
,只为容易理解,读者可根据自己需求进行重构代码、优化代码。
- 使用到
{{#var}}
单个表格渲染(无模板)- 使用到
{{+var}}
多个表格渲染-嵌套模板(无模板)- 使用到
{{var}}}
单个表格渲染-标记表(有模板)- 使用到
{{?sections}}{{/sections}}
多个表格渲染-标记位置(有模板)
导出的word中存在
单个
表格, 或动态的多个
表格
Demo: boot项目实现业务===>服务器信息表格
项目依赖引入poi-tl
依赖
<!-- poi-tl -->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.0</version>
</dependency>
<!-- 牛*的lombok插件库 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 牛*的hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.14</version>
</dependency>
poi-tl提供了抽象表格策略类 DynamicTableRenderPolicy
我们可以自定义模板渲染策略类,继承即可,从而动态渲染的部分单元格,实现我们需求
-- 注意点:
{{serverListTable}}可以放到表格中任意处,可识别到该表格即可
- 新建数据存储实体类-
ServerTableData
-
@Data
public class ServerTableData {
/*
携带表格中真实数据
*/
private List<RowRenderData> serverDataList;
/*
携带要分组的信息
*/
private List<Map<String, Object>> groupDataList;
}
- 新建自定义表格渲染策略类-
ServerTablePolicy
-
public class ServerTablePolicy extends DynamicTableRenderPolicy {
@Override
public void render(XWPFTable xwpfTable, Object tableData) throws Exception {
if (null == tableData) {
return;
}
// 参数数据声明
ServerTableData serverTableData = (ServerTableData) tableData;
List<RowRenderData> serverDataList = serverTableData.getServerDataList();
List<Map<String, Object>> groupDataList = serverTableData.getGroupDataList();
if (CollUtil.isNotEmpty(serverDataList)) {
// 先删除一行, demo中第一行是为了调整 三线表 样式
xwpfTable.removeRow(1);
// 行从中间插入, 因此采用倒序渲染数据
for (int i = serverDataList.size() - 1; i >= 0; i--) {
XWPFTableRow newRow = xwpfTable.insertNewTableRow(1);
newRow.setHeight(400);
for (int j = 0; j < 4; j++) {
newRow.createCell();
}
// 渲染一行数据
TableRenderPolicy.Helper.renderRow(newRow, serverDataList.get(i));
}
// 处理合并
for (int i = 0; i < serverDataList.size(); i++) {
// 获取要合并的名称那一列数据 (样本采用第一列)
String typeNameData = serverDataList.get(i).getCells().get(0).getParagraphs().get(0).getContents().get(0).toString();
for (int j = 0; j < groupDataList.size(); j++) {
String typeNameTemplate = String.valueOf(groupDataList.get(j).get("typeName"));
int listSize = Integer.parseInt(String.valueOf(groupDataList.get(j).get("listSize")));
// 若匹配上 就直接合并
if (typeNameTemplate.equals(typeNameData)) {
TableTools.mergeCellsVertically(xwpfTable, 0, i + 1, i + listSize);
groupDataList.remove(j);
break;
}
}
// 生成行数据时候已经设置水平, 在此处进行垂直居中
for (int j = 0; j < 4; j++){
XWPFTableCell cell = xwpfTable.getRow(i + 1).getCell(j);
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
}
}
}
}
}
- 新建业务实现类(数据来源)-
ExportWordServiceImpl
-
@Service("exportWord")
public class ExportWordServiceImpl implements ExportWordService {
/**
* @Description: 单个表格数据渲染(测试 合并单元格)
*/
@Override
public XWPFTemplate oneTable() {
// 获取模板文件流
InputStream resourceAsStream =
this.getClass().getResourceAsStream("/templates/serverInfo.docx");
// word数据集合
Map<String, Object> params = new HashMap<>();
// 伪造一个表格数据
ServerTableData tableData = getServerTableData();
// 赋值, word解析
params.put("serverListTable", tableData);
Configure configure = Configure.builder()
.bind("serverListTable", new ServerTablePolicy())
.build();
return XWPFTemplate.compile(Objects.requireNonNull(resourceAsStream), configure).render(params);
}
/**
* @Description: 动态多个表格
*/
@Override
public XWPFTemplate dynamicTable() {
// 获取模板文件流
InputStream resourceAsStream =
this.getClass().getResourceAsStream("/templates/dynamicServerInfo.docx");
// word数据集合
Map<String, Object> params = new HashMap<>();
List<Map<String, Object>> dynamicFlag = new ArrayList<>();
// 伪造3个表格数据
for (int i = 0; i < 3; i++) {
ServerTableData tableData = getServerTableData();
Map<String, Object> dynamicTableMap = new HashMap<>();
dynamicTableMap.put("serverListTable", tableData);
dynamicTableMap.put("firstTitle", "一级标题");
dynamicTableMap.put("secondTitle", "二级标题");
dynamicTableMap.put("tableName", "表名");
dynamicFlag.add(dynamicTableMap);
}
// 赋值, word解析
params.put("dynamicFlag", dynamicFlag);
Configure configure = Configure.builder()
.bind("serverListTable", new ServerTablePolicy())
.build();
return XWPFTemplate.compile(Objects.requireNonNull(resourceAsStream), configure).render(params);
}
/**
* @Description: 模拟表格中的真实数据
*/
private ServerTableData getServerTableData() {
ServerTableData serverTableData = new ServerTableData();
List<RowRenderData> serverDataList = new ArrayList<>();
for (int j = 0; j < 4; j++) {
String typeName = "数据库服务器";
if (j > 1) {
typeName = "应用服务器";
}
RowRenderData serverData = Rows.of(typeName, "4c处理器", "Win11", "127.0.0.1").center().create();
serverDataList.add(serverData);
}
List<Map<String, Object>> groupDataList = new ArrayList<>();
Map<String, Object> groupData1 = new HashMap<>();
groupData1.put("typeName", "数据库服务器");
groupData1.put("listSize", "2");
Map<String, Object> groupData2 = new HashMap<>();
groupData2.put("typeName", "应用服务器");
groupData2.put("listSize", "2");
groupDataList.add(groupData1);
groupDataList.add(groupData2);
serverTableData.setServerDataList(serverDataList);
serverTableData.setGroupDataList(groupDataList);
return serverTableData;
}
}
- 新建接口类,实现下载word-
ExportWordController
-
@Slf4j
@RestController
@RequestMapping("/poi")
public class ExportWordController {
@Resource(name = "exportWord")
private ExportWordService exportWordService;
/**
* @Description: 合并单元格
*/
@GetMapping("one-table")
public void oneTable() {
poiExportWord("单个表格数据渲染.docx", exportWordService.oneTable());
}
/**
* @Description: 多表格渲染
*/
@GetMapping("dynamic-table")
public void dynamicTable() {
poiExportWord("多表格数据渲染.docx", exportWordService.dynamicTable());
}
// 导出文档Header信息
public static final String CONTENT_DISPOSITION_KEY = "Content-disposition";
public static final String CONTENT_DISPOSITION_VALUE = "attachment; filename={};filename*=utf-8''{}";
public static final String URL_ENCODER_CHARSET = "UTF-8";
@Resource
private HttpServletResponse response;
/**
* @Description: POI-导出word文档
* @Params: [fileName, xwpfTemplate] 文件名称, poi-word解析模板
*/
@SneakyThrows(value = Exception.class)
private void poiExportWord(String fileName, XWPFTemplate xwpfTemplate) {
String encodeFileName = URLEncoder.encode(fileName, URL_ENCODER_CHARSET);
// 弹出下载框并且回填文件名
response.setHeader(CONTENT_DISPOSITION_KEY,
StrUtil.format(CONTENT_DISPOSITION_VALUE, encodeFileName, encodeFileName));
xwpfTemplate.write(response.getOutputStream());
xwpfTemplate.close();
}
}
接口调用
:
下面代码依旧在上面的接口类,实现类中
ExportWordController、ExportWordServiceImpl
private static final String[] SERVER_HEADER = new String[]{"设备名称","硬件配置","软件版本","IP和网关地址"};
@Override
public XWPFTemplate oneTableNo() {
// 获取模板文件流
InputStream resourceAsStream =
this.getClass().getResourceAsStream("/templates/oneTableWithout.docx");
// word数据集合
Map<String, Object> params = new HashMap<>();
// 表格样式
BorderStyle borderStyle = new BorderStyle();
borderStyle.setColor("000000");
borderStyle.setSize(5);
borderStyle.setType(XWPFTable.XWPFBorderType.SINGLE);
// 单元格合并声明
MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = MergeCellRule.builder();
mergeCellRuleBuilder.map(MergeCellRule.Grid.of(1, 0), MergeCellRule.Grid.of(2, 0));
mergeCellRuleBuilder.map(MergeCellRule.Grid.of(3, 0), MergeCellRule.Grid.of(4, 0));
RowRenderData serverHeader = getTableHeaderRow(SERVER_HEADER);
Tables.TableBuilder tableBuilder = Tables.ofA4MediumWidth().addRow(serverHeader);
RowRenderData serverRow1 = Rows.of("数据库服务器", "4c处理器", "Win11", "127.0.0.1")
.center().textFontSize(10).create();
RowRenderData serverRow2 = Rows.of("数据库服务器", "4c处理器", "Win11", "127.0.0.1")
.center().textFontSize(10).create();
RowRenderData serverRow3 = Rows.of("应用服务器", "4c处理器", "Win11", "127.0.0.1")
.center().textFontSize(10).create();
RowRenderData serverRow4 = Rows.of("应用服务器", "4c处理器", "Win11", "127.0.0.1")
.center().textFontSize(10).create();
TableRenderData serverTable = tableBuilder.addRow(serverRow1).addRow(serverRow2).addRow(serverRow3)
.addRow(serverRow4).border(borderStyle).center().create();
serverTable.setMergeRule(mergeCellRuleBuilder.build());
params.put("serverListTable", serverTable);
return XWPFTemplate.compile(Objects.requireNonNull(resourceAsStream)).render(params);
}
/**
* 表头行 统一样式
*/
private RowRenderData getTableHeaderRow(String[] header) {
return Rows.of(header).center()
.textColor("000000").textFontSize(10).textBold().create();
}
/**
* @Description: 单个表格-无需模板
*/
@GetMapping("one-table-no")
public void oneTableNo() {
poiExportWord("无模板单个表格渲染.docx", exportWordService.oneTableNo());
}
接口调用
:
模板
: 两个,一个主模板,一个是内嵌模板(用于生成多个表格)
// 代码基本与单个是一致的, 下面只展示差异部分
// 将子模板流赋值给主模板参数
InputStream innerInputStream =
this.getClass().getResourceAsStream("/templates/inner.docx");
DocxRenderData innerData = new DocxRenderData(innerInputStream, dataList);
params.put("innerTable", innerData);
/*
注意这里的dataList为 List
// 获取主模板文件流(后续代码真正使用的流)
InputStream resourceAsStream =
this.getClass().getResourceAsStream("/templates/dynamicTableWithout.docx");
如若需要本人项目代码-可评论联系me.
根据模板-渲染表格数据是参考该大牛写的文章实现的:
https://blog.csdn.net/qq_26383975/article/details/112238802