当前位置:   article > 正文

springboot+poi-tl导出复杂word,合并单元格,图片_com.deepoove.poi.exception.renderexception: templa

com.deepoove.poi.exception.renderexception: templaterenderpolicy render erro

最近遇到html转word的需求,遍寻全网没找到简单易用的demo,于是打算自己写一个

springboot版本

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.1.10.RELEASE</version>
  5. <relativePath/>
  6. </parent>

依赖如下:

  1. <!-- 增加poi-tl依赖-->
  2. <dependency>
  3. <groupId>com.deepoove</groupId>
  4. <artifactId>poi-tl</artifactId>
  5. <version>1.10.4</version>
  6. </dependency>

其他依赖(仅供参考)

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-web</artifactId>
  8. <exclusions>
  9. <exclusion>
  10. <artifactId>log4j-to-slf4j</artifactId>
  11. <groupId>org.apache.logging.log4j</groupId>
  12. </exclusion>
  13. </exclusions>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter-jdbc</artifactId>
  18. </dependency>
  19. <dependency>
  20. <groupId>mysql</groupId>
  21. <artifactId>mysql-connector-java</artifactId>
  22. <version>8.0.16</version>
  23. </dependency>
  24. <dependency>
  25. <groupId>com.baomidou</groupId>
  26. <artifactId>mybatis-plus-boot-starter</artifactId>
  27. <version>3.5.1</version>
  28. </dependency>
  29. <dependency>
  30. <groupId>com.baomidou</groupId>
  31. <artifactId>mybatis-plus-generator</artifactId>
  32. <version>3.5.2</version>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.apache.velocity</groupId>
  36. <artifactId>velocity-engine-core</artifactId>
  37. <version>2.1</version>
  38. </dependency>
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-starter-data-redis</artifactId>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.apache.commons</groupId>
  45. <artifactId>commons-pool2</artifactId>
  46. <version>2.7.0</version>
  47. </dependency>

接口如下:

  1. /**
  2. * 导出word
  3. */
  4. @PostMapping("patrolRecordReport")
  5. public void patrolRecordReport(@RequestBody IdDTO idDTO, HttpServletResponse response)
  6. {
  7. recordService.patrolRecordReport(response, idDTO.getId());
  8. }

Service如下:

  1. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  2. import com.deepoove.poi.XWPFTemplate;
  3. import com.deepoove.poi.config.Configure;
  4. import com.deepoove.poi.data.PictureRenderData;
  5. import com.deepoove.poi.data.Pictures;
  6. import org.springframework.core.io.ClassPathResource;
  7. import org.springframework.stereotype.Service;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. import java.io.OutputStream;
  12. import java.util.*;
  13. import java.util.stream.Collectors;
  14. @Service
  15. public class RecordService {
  16. private final INsjDeviceValueInfoService deviceValueInfoService;
  17. private final INsjPatrolRecordService patrolRecordService;
  18. private final INsjPatrolRecordPointValueService recordPointValueService;
  19. private final INsjPatrolPointInfoService patrolPointInfoService;
  20. private final INsjDeviceInfoService deviceInfoService;
  21. private final INsjPatrolInfoService patrolInfoService;
  22. private final INsjPatrolRecordPointService recordPointService;
  23. public RecordService(INsjDeviceValueInfoService deviceValueInfoService, INsjPatrolRecordService patrolRecordService, INsjPatrolRecordPointValueService recordPointValueService, INsjPatrolPointInfoService patrolPointInfoService, INsjDeviceInfoService deviceInfoService, INsjPatrolInfoService patrolInfoService, INsjPatrolRecordPointService recordPointService) {
  24. this.deviceValueInfoService = deviceValueInfoService;
  25. this.patrolRecordService = patrolRecordService;
  26. this.recordPointValueService = recordPointValueService;
  27. this.patrolPointInfoService = patrolPointInfoService;
  28. this.deviceInfoService = deviceInfoService;
  29. this.patrolInfoService = patrolInfoService;
  30. this.recordPointService = recordPointService;
  31. }
  32. //获取导出word要用到数据源map
  33. public Map<String, Object> getData(String id) {
  34. Map<String, Object> dataMap = new HashMap<>();
  35. List<PatrolRecordReportVO> patrolRecordReportVOS = new ArrayList<>();
  36. NsjPatrolRecord patrolRecord = patrolRecordService.getById(id);//任务记录
  37. List<NsjPatrolPointInfo> pointInfos = patrolPointInfoService.list(
  38. new QueryWrapper<NsjPatrolPointInfo>().eq("patrol_id", patrolRecord.getPatrolId()));
  39. Map<Long, String> pointMap = pointInfos.stream().collect
  40. (Collectors.toMap(NsjPatrolPointInfo::getId, NsjPatrolPointInfo::getName));
  41. List<NsjDeviceValueInfo> deviceValueInfos = deviceValueInfoService.list();
  42. Map<Long, String> deviceValueMap = deviceValueInfos.stream().collect
  43. (Collectors.toMap(NsjDeviceValueInfo::getId, NsjDeviceValueInfo::getName));
  44. Map<Long, Integer> dataTypeMap = deviceValueInfos.stream().collect
  45. (Collectors.toMap(NsjDeviceValueInfo::getId, NsjDeviceValueInfo::getDataType));
  46. List<NsjPatrolRecordPointValue> recordPointValues = recordPointValueService.list(
  47. new QueryWrapper<NsjPatrolRecordPointValue>().eq("record_id", id).orderByAsc("device_id"));
  48. List<NsjDeviceInfo> nsjDeviceInfos = deviceInfoService.list();//多个设备
  49. Map<Long, String> deviceMap = nsjDeviceInfos.stream().collect
  50. (Collectors.toMap(NsjDeviceInfo::getId, NsjDeviceInfo::getName));
  51. List<NsjPatrolRecordPoint> recordPoints = recordPointService.list();//多个点
  52. Map<Long, String> recordPointsMap = recordPoints.stream().collect
  53. (Collectors.toMap(NsjPatrolRecordPoint::getPointId, NsjPatrolRecordPoint::getCapture));
  54. for (int i = 0; i < recordPointValues.size(); i++) {
  55. NsjPatrolRecordPointValue recordPointValue = recordPointValues.get(i);
  56. PictureRenderData img = null;
  57. if (i != 0) {
  58. NsjPatrolRecordPointValue recordPointValue1 = recordPointValues.get(i - 1);
  59. if (!recordPointValue.getPointId().equals(recordPointValue1.getPointId())) {
  60. img = Pictures.ofLocal(recordPointsMap.get(recordPointValue.getPointId())).fitSize().create();
  61. }
  62. } else {
  63. img = Pictures.ofLocal(recordPointsMap.get(recordPointValue.getPointId())).fitSize().create();
  64. }
  65. PatrolRecordReportVO recordReportVO = new PatrolRecordReportVO(
  66. pointMap.get(recordPointValue.getPointId()), deviceMap.get(recordPointValue.getDeviceId()),
  67. deviceValueMap.get(recordPointValue.getValueId()), dataTypeMap.get(recordPointValue.getValueId()) == 1 ?
  68. recordPointValue.getValue().intValue() + "" : recordPointValue.getValue() + "", img);
  69. patrolRecordReportVOS.add(recordReportVO);
  70. }
  71. //获取名称,开始时间,结束时间
  72. NsjPatrolInfo patrolInfo = patrolInfoService.getById(patrolRecord.getPatrolId());
  73. //人员信息
  74. dataMap.put("list", patrolRecordReportVOS);
  75. dataMap.put("patrolName", patrolInfo.getName());
  76. dataMap.put("startTime", MyDateUtil.date2String(patrolRecord.getStartTime(), null));
  77. dataMap.put("endTime", MyDateUtil.date2String(patrolRecord.getEndTime(), null));
  78. return dataMap;
  79. }
  80. //导出word,返回文件流
  81. public void patrolRecordReport(HttpServletResponse response, String id) {
  82. Map<String, Object> dataMap = getData(id);
  83. //给需要单独处理的表格绑定处理策略
  84. Configure config = Configure.builder().bind("list", new DefineMethodPolicy()).build();
  85. InputStream input = null;
  86. try {
  87. input = new ClassPathResource("templates\\template.docx").getInputStream();
  88. } catch (IOException e) {
  89. e.printStackTrace();
  90. }
  91. XWPFTemplate template = XWPFTemplate.compile(input, config).render(dataMap);
  92. // 生成的word格式
  93. String formatSuffix = ".docx";
  94. // 拼接后的文件名
  95. String fileName = "GreenHouseGas" + formatSuffix;//文件名
  96. try {
  97. //=================生成word到设置浏览默认下载地址=================
  98. // 设置强制下载不打开
  99. response.setContentType("application/force-download");
  100. // 设置文件名
  101. response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
  102. OutputStream out = response.getOutputStream();
  103. template.write(out);
  104. out.flush();
  105. out.close();
  106. template.close();
  107. } catch (Exception e) {
  108. e.printStackTrace();
  109. }
  110. }
  111. }

导出word及处理策略类如下:

  1. import com.deepoove.poi.XWPFTemplate;
  2. import com.deepoove.poi.data.RowRenderData;
  3. import com.deepoove.poi.data.Rows;
  4. import com.deepoove.poi.exception.RenderException;
  5. import com.deepoove.poi.policy.RenderPolicy;
  6. import com.deepoove.poi.render.compute.EnvModel;
  7. import com.deepoove.poi.render.compute.RenderDataCompute;
  8. import com.deepoove.poi.render.processor.DocumentProcessor;
  9. import com.deepoove.poi.render.processor.EnvIterator;
  10. import com.deepoove.poi.resolver.TemplateResolver;
  11. import com.deepoove.poi.template.ElementTemplate;
  12. import com.deepoove.poi.template.MetaTemplate;
  13. import com.deepoove.poi.template.run.RunTemplate;
  14. import com.deepoove.poi.util.ReflectionUtils;
  15. import com.deepoove.poi.util.TableTools;
  16. import org.apache.poi.xwpf.usermodel.*;
  17. import org.apache.xmlbeans.XmlCursor;
  18. import org.apache.xmlbeans.XmlObject;
  19. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
  20. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
  21. import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
  22. import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
  23. import org.springframework.util.CollectionUtils;
  24. import java.util.ArrayList;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.stream.Collectors;
  29. /**
  30. * @Description
  31. * @Author admin
  32. */
  33. public class DefineMethodPolicy implements RenderPolicy {
  34. private String prefix;
  35. private String suffix;
  36. private boolean onSameLine;
  37. public DefineMethodPolicy() {
  38. this(false);
  39. }
  40. public DefineMethodPolicy(boolean onSameLine) {
  41. this("[", "]", onSameLine);
  42. }
  43. public DefineMethodPolicy(String prefix, String suffix) {
  44. this(prefix, suffix, false);
  45. }
  46. public DefineMethodPolicy(String prefix, String suffix, boolean onSameLine) {
  47. this.prefix = prefix;
  48. this.suffix = suffix;
  49. this.onSameLine = onSameLine;
  50. }
  51. @Override
  52. public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
  53. RunTemplate runTemplate = (RunTemplate) eleTemplate;
  54. XWPFRun run = runTemplate.getRun();
  55. try {
  56. if (!TableTools.isInsideTable(run)) {
  57. throw new IllegalStateException("The template tag " + runTemplate.getSource() + " must be inside a table");
  58. } else {
  59. XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
  60. XWPFTable table = tagCell.getTableRow().getTable();
  61. run.setText("", 0);
  62. int templateRowIndex = this.getTemplateRowIndex(tagCell);
  63. if (null != data && data instanceof Iterable) {
  64. Iterator<?> iterator = ((Iterable) data).iterator();
  65. XWPFTableRow templateRow = table.getRow(templateRowIndex);
  66. TemplateResolver resolver = new TemplateResolver(template.getConfig().copy(this.prefix, this.suffix));
  67. boolean firstFlag = true;
  68. int index = 0;
  69. boolean hasNext = iterator.hasNext();
  70. while (hasNext) {
  71. Object root = iterator.next();
  72. hasNext = iterator.hasNext();
  73. int insertPosition = templateRowIndex++;
  74. table.insertNewTableRow(insertPosition);
  75. this.setTableRow(table, templateRow, insertPosition);
  76. XmlCursor newCursor = templateRow.getCtRow().newCursor();
  77. newCursor.toPrevSibling();
  78. XmlObject object = newCursor.getObject();
  79. XWPFTableRow nextRow = new XWPFTableRow((CTRow) object, table);
  80. if (!firstFlag) {
  81. List<XWPFTableCell> tableCells = nextRow.getTableCells();
  82. Iterator var21 = tableCells.iterator();
  83. while (var21.hasNext()) {
  84. XWPFTableCell cell = (XWPFTableCell) var21.next();
  85. CTTcPr tcPr = TableTools.getTcPr(cell);
  86. CTVMerge vMerge = tcPr.getVMerge();
  87. if (null != vMerge && STMerge.RESTART == vMerge.getVal()) {
  88. vMerge.setVal(STMerge.CONTINUE);
  89. }
  90. }
  91. } else {
  92. firstFlag = false;
  93. }
  94. this.setTableRow(table, nextRow, insertPosition);
  95. RenderDataCompute dataCompute = template.getConfig().getRenderDataComputeFactory().
  96. newCompute(EnvModel.of(root, EnvIterator.makeEnv(index++, hasNext)));
  97. List<XWPFTableCell> cells = nextRow.getTableCells();
  98. cells.forEach((cellx) -> {
  99. List<MetaTemplate> templates = resolver.resolveBodyElements(cellx.getBodyElements());
  100. (new DocumentProcessor(template, resolver, dataCompute)).process(templates);
  101. });
  102. }
  103. }
  104. table.removeRow(templateRowIndex);
  105. this.afterloop(table, data);
  106. }
  107. } catch (Exception var25) {
  108. throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + var25.getMessage(), var25);
  109. }
  110. }
  111. private int getTemplateRowIndex(XWPFTableCell tagCell) {
  112. XWPFTableRow tagRow = tagCell.getTableRow();
  113. return this.onSameLine ? this.getRowIndex(tagRow) : this.getRowIndex(tagRow) + 1;
  114. }
  115. protected void afterloop(XWPFTable table, Object data) {
  116. if (null == data) {
  117. return;
  118. }
  119. List<PatrolRecordReportVO> defineMethodList = null;
  120. try {
  121. defineMethodList = (List<PatrolRecordReportVO>) data;
  122. } catch (Exception e) {
  123. e.printStackTrace();
  124. }
  125. Map<String, List<PatrolRecordReportVO>> unitDefineMethodMap = defineMethodList.stream().collect(Collectors.groupingBy(PatrolRecordReportVO::getPoint));
  126. Map<String, List<PatrolRecordReportVO>> unitParamMap = defineMethodList.stream().collect(Collectors.groupingBy(defineMethod -> defineMethod.getPoint() + "-" + defineMethod.getDevice()));
  127. if (!CollectionUtils.isEmpty(defineMethodList)) {
  128. List<RowRenderData> dataList = new ArrayList<>();
  129. for (PatrolRecordReportVO tmp : defineMethodList) {
  130. RowRenderData renderData = Rows.create(tmp.getPoint(), tmp.getDevice(), tmp.getDeviceValue(), tmp.getValue().toString());
  131. dataList.add(renderData);
  132. }
  133. List<String> unitList = null;
  134. try {
  135. unitList = JacksonUtils.json2list(JacksonUtils.obj2json(unitDefineMethodMap.keySet()), String.class);
  136. } catch (Exception e) {
  137. e.printStackTrace();
  138. }
  139. List<String> paramList = null;
  140. try {
  141. paramList = JacksonUtils.json2list(JacksonUtils.obj2json(unitParamMap.keySet()), String.class);
  142. } catch (Exception e) {
  143. e.printStackTrace();
  144. }
  145. //处理合并
  146. for (int i = 0; i < dataList.size(); i++) {
  147. //处理第一列合并
  148. Object v = dataList.get(i).getCells().get(0).getParagraphs().get(0).getContents().get(0);
  149. String unit_name = String.valueOf(v);
  150. for (int j = 0; j < unitList.size(); j++) {
  151. String unitId = unitList.get(j);
  152. List<PatrolRecordReportVO> patrolRecordReportVOS = unitDefineMethodMap.get(unitId);
  153. String unitName = patrolRecordReportVOS.get(0).getPoint();
  154. if (unit_name.equals(unitName) && patrolRecordReportVOS.size() > 1) {
  155. // 合并第0列的第i+1行到第i+unitSize行的单元格
  156. TableTools.mergeCellsVertically(table, 0, i + 1, i + patrolRecordReportVOS.size());
  157. TableTools.mergeCellsVertically(table, 4, i + 1, i + patrolRecordReportVOS.size());
  158. //处理垂直居中
  159. for (int y = 0; y < 5; y++) {
  160. XWPFTableCell cell = table.getRow(i + 2).getCell(y);
  161. cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); //垂直居中
  162. }
  163. unitList.remove(j);
  164. break;
  165. }
  166. }
  167. //处理第二列合并
  168. Object v1 = dataList.get(i).getCells().get(1).getParagraphs().get(0).getContents().get(0);
  169. String paramType = v + "-" + v1;
  170. for (int j = 0; j < paramList.size(); j++) {
  171. String key = paramList.get(j);
  172. List<PatrolRecordReportVO> tmpList = unitParamMap.get(key);
  173. String paramName = tmpList.get(0).getPoint() + "-" + tmpList.get(0).getDevice();
  174. if (paramType.equals(paramName) && tmpList.size() > 1) {
  175. // 合并第1列的第i+1行到第i+unitSize行的单元格
  176. TableTools.mergeCellsVertically(table, 1, i + 1, i + tmpList.size());
  177. //处理垂直居中
  178. for (int y = 0; y < 5; y++) {
  179. XWPFTableCell cell = table.getRow(i + 2).getCell(y);
  180. cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); //垂直居中
  181. }
  182. paramList.remove(j);
  183. break;
  184. }
  185. }
  186. }
  187. }
  188. }
  189. private void setTableRow(XWPFTable table, XWPFTableRow templateRow, int pos) {
  190. List<XWPFTableRow> rows = (List) ReflectionUtils.getValue("tableRows", table);
  191. rows.set(pos, templateRow);
  192. table.getCTTbl().setTrArray(pos, templateRow.getCtRow());
  193. }
  194. private int getRowIndex(XWPFTableRow row) {
  195. List<XWPFTableRow> rows = row.getTable().getRows();
  196. return rows.indexOf(row);
  197. }
  198. }
  

word模板如下:

名称:{{patrolName}}

开始时间:{{startTime}}

结束时间:{{endTime}}

{{list}}点位

设备

测量值

数据

图片

[point]

[device]

[deviceValue]

[value]

[@img]

数据模型类如下:

  1. import com.deepoove.poi.data.PictureRenderData;
  2. public class PatrolRecordReportVO {
  3. private String point;//点位
  4. private String device;//设备
  5. private String deviceValue;//测量值
  6. private String value;//数据
  7. private PictureRenderData img;//图片
  8. public PatrolRecordReportVO() {
  9. }
  10. public PatrolRecordReportVO(String point, String device, String deviceValue, String value, PictureRenderData img) {
  11. this.point = point;
  12. this.device = device;
  13. this.deviceValue = deviceValue;
  14. this.value = value;
  15. this.img = img;
  16. }
  17. public String getPoint() {
  18. return point;
  19. }
  20. public void setPoint(String point) {
  21. this.point = point;
  22. }
  23. public String getDevice() {
  24. return device;
  25. }
  26. public void setDevice(String device) {
  27. this.device = device;
  28. }
  29. public String getDeviceValue() {
  30. return deviceValue;
  31. }
  32. public void setDeviceValue(String deviceValue) {
  33. this.deviceValue = deviceValue;
  34. }
  35. public String getValue() {
  36. return value;
  37. }
  38. public void setValue(String value) {
  39. this.value = value;
  40. }
  41. public PictureRenderData getImg() {
  42. return img;
  43. }
  44. public void setImg(PictureRenderData img) {
  45. this.img = img;
  46. }
  47. }

Poi-tl官网链接http://deepoove.com/poi-tl/

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/606768
推荐阅读
相关标签
  

闽ICP备14008679号