文章目录
poi-tl介绍
官方文档地址:http://deepoove.com/poi-tl/
源码地址:https://github.com/Sayi/poi-tl
poi-tl(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档。
最近在做项目时候有一个关于导出Word的文件的需求,需要导出的word文件较大,并且格式比较复杂,使用poi-tl可以很好的解决。在这里记录一下关于复杂表格的合并与生成。
poi-tl的优势
poi-tl 是基于 Apache POI ,使用时请注意poi的版本依赖冲突问题
一、快速开始
1. 添加依赖
<!--poi-tl--><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.0</version></dependency>
2.快速入门
新建Word文档template.docx,这里模板文件存放resources/word目录下,模板包含标签 {{title}}
代码示例
@GetMapping("/export")publicvoidexport(HttpServletResponse response)throwsIOException{// 获取模板文件流InputStream resourceAsStream =this.getClass().getResourceAsStream("/word/template.docx");//poi-tl 配置ConfigureBuilder builder =Configure.builder();
builder.useSpringEL(false);Map<String,Object> map =newHashMap<>();
map.put("title","hello,poi-tl!");XWPFTemplate template =XWPFTemplate.compile(Objects.requireNonNull(resourceAsStream), builder.build()).render(map);//输出文件流
template.writeAndClose(newFileOutputStream("D:\\output.docx"));}
3.输出
可以写到任意输出流中,比如文件流:
template.write(newFileOutputStream("output.docx"));
比如网络流:
//输出网络流
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment;filename=\""+"out_template.docx"+"\"");// HttpServletResponse responseOutputStream out = response.getOutputStream();BufferedOutputStream bos =newBufferedOutputStream(out);
template.write(bos);
bos.flush();
out.flush();PoitlIOUtils.closeQuietlyMulti(template, bos, out);
二、表格合并
功能需求
导出的word中存在
单个
表格, 或
动态的多个
表格
word模板
poi-tl提供了抽象表格策略类
DynamicTableRenderPolicy
我们可以自定义模板渲染策略类,继承即可,从而动态渲染的部分单元格,实现我们需求
代码实现
1.新建数据存储实体类-
ServerTableData
@DatapublicclassServerTableData{/**
* 携带表格中真实数据
*/privateList<RowRenderData> serverDataList;/**
* 携带要分组的信息
*/privateList<Map<String,Object>> groupDataList;/**
* 需要合并的列,从0开始
*/privateInteger mergeColumn;}
2.新建自定义表格渲染策略类-
ServerTablePolicy
publicclassServerTablePolicyextendsDynamicTableRenderPolicy{@Overridepublicvoidrender(XWPFTable xwpfTable,Object tableData)throwsException{if(null== tableData){return;}// 参数数据声明ServerTableData serverTableData =(ServerTableData) tableData;List<RowRenderData> serverDataList = serverTableData.getServerDataList();List<Map<String,Object>> groupDataList = serverTableData.getGroupDataList();Integer mergeColumn = serverTableData.getMergeColumn();if(CollectionUtils.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++){// 获取要合并的名称那一列数据 mergeColumn代表要合并的列,从0开始String typeNameData = serverDataList.get(i).getCells().get(mergeColumn).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;}}}}}}
3.接口类
@GetMapping("/export")publicvoidexport(HttpServletResponse response)throwsIOException{// 获取模板文件流InputStream resourceAsStream =this.getClass().getResourceAsStream("/word/template.docx");//poi-tl 配置ConfigureBuilder builder =Configure.builder();
builder.useSpringEL(false);Map<String,Object> map =newHashMap<>();// 伪造一个表格数据//单个表格ServerTableData oneTable =getServerTableData();
map.put("oneTable",oneTable);
builder.bind("oneTable",newServerTablePolicy());//多个表格List<Map<String,Object>> dynamicFlag =newArrayList<>();// 伪造3个表格数据for(int i =0; i <3; i++){ServerTableData tableData =getServerTableData();Map<String,Object> dynamicTableMap =newHashMap<>();
dynamicTableMap.put("serverListTable", tableData);
dynamicTableMap.put("tableName","表名");
dynamicFlag.add(dynamicTableMap);}
map.put("listTable",dynamicFlag);
builder.bind("serverListTable",newServerTablePolicy());XWPFTemplate template =XWPFTemplate.compile(Objects.requireNonNull(resourceAsStream), builder.build()).render(map);//输出网络流
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition","attachment;filename=\""+"out_template.docx"+"\"");// HttpServletResponse responseOutputStream out = response.getOutputStream();BufferedOutputStream bos =newBufferedOutputStream(out);
template.write(bos);
bos.flush();
out.flush();PoitlIOUtils.closeQuietlyMulti(template, bos, out);}privateServerTableDatagetServerTableData(){ServerTableData serverTableData =newServerTableData();List<RowRenderData> serverDataList =newArrayList<>();for(int j =0; j <4; j++){String typeName;RowRenderData serverData;if(j >1){
typeName ="索隆";
serverData =Rows.of(typeName,"喝酒","三千世界","无").center().create();}else{
typeName ="路飞";
serverData =Rows.of(typeName,"大鸡腿","巨人手枪","橡胶果实").center().create();}
serverDataList.add(serverData);}List<Map<String,Object>> groupDataList =newArrayList<>();Map<String,Object> groupData1 =newHashMap<>();
groupData1.put("typeName","索隆");
groupData1.put("listSize","2");Map<String,Object> groupData2 =newHashMap<>();
groupData2.put("typeName","路飞");
groupData2.put("listSize","2");
groupDataList.add(groupData1);
groupDataList.add(groupData2);
serverTableData.setServerDataList(serverDataList);
serverTableData.setGroupDataList(groupDataList);
serverTableData.setMergeColumn(0);return serverTableData;}
4.效果图
版权归原作者 ufo21 所有, 如有侵权,请联系我们删除。