当前位置:   article > 正文

Java根据word 模版生成 word文件,支持 循环多图片及表格多图片 支持按照标记去除表格第一行表头_java使用word作为导出word模板word中如何写循环

java使用word作为导出word模板word中如何写循环

模版文件

jar包

  1. <dependency>
  2. <groupId>com.deepoove</groupId>
  3. <artifactId>poi-tl</artifactId>
  4. <version>1.12.1</version>
  5. </dependency>

可能会出现poi jar包冲突 如出现 将老版本的poi排掉就好 ,也可能出现 log4j 版本低 用下面这个就行

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-api</artifactId>
  4. <version>2.17.1</version>
  5. </dependency>

代码

  1. package com.util;
  2. import com.deepoove.poi.XWPFTemplate;
  3. import com.deepoove.poi.config.Configure;
  4. import com.deepoove.poi.config.ConfigureBuilder;
  5. import com.deepoove.poi.data.HyperlinkTextRenderData;
  6. import com.deepoove.poi.data.PictureRenderData;
  7. import com.deepoove.poi.data.Pictures;
  8. import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
  9. import com.deepoove.poi.util.PoitlIOUtils;
  10. import com.zjjw.platform.core.exception.BusinessRuntimeException;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.apache.commons.collections.CollectionUtils;
  13. import org.apache.poi.xwpf.usermodel.XWPFDocument;
  14. import org.apache.poi.xwpf.usermodel.XWPFTable;
  15. import org.apache.poi.xwpf.usermodel.XWPFTableCell;
  16. import org.apache.poi.xwpf.usermodel.XWPFTableRow;
  17. import org.springframework.core.io.ClassPathResource;
  18. import javax.servlet.http.HttpServletResponse;
  19. import java.io.BufferedOutputStream;
  20. import java.io.ByteArrayInputStream;
  21. import java.io.ByteArrayOutputStream;
  22. import java.io.Closeable;
  23. import java.io.IOException;
  24. import java.io.InputStream;
  25. import java.io.OutputStream;
  26. import java.io.UnsupportedEncodingException;
  27. import java.net.HttpURLConnection;
  28. import java.net.URL;
  29. import java.net.URLConnection;
  30. import java.net.URLEncoder;
  31. import java.nio.file.Files;
  32. import java.nio.file.Paths;
  33. import java.util.ArrayList;
  34. import java.util.Arrays;
  35. import java.util.HashMap;
  36. import java.util.LinkedHashMap;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Objects;
  40. import java.util.UUID;
  41. import java.util.stream.Collectors;
  42. /**
  43. * POI-TL 进行转换 支持多图片及循环增加表格
  44. * <p>
  45. * 使用方式:
  46. * 参数传入类型 Map<String,Object>
  47. * 模版 纯文本 为:
  48. * {{name}} 则map.put("name",params) name 是自己定义的名字 下面的都是自己定义名字,只是示例
  49. * <p>
  50. * 图片为:
  51. * {{@imgUrl}} 只要是 图片的 都需在参数前 加 @ 符号 这个只支持单张图片!!! map.put("imgUrl",params)
  52. * <p>
  53. * 循环表格:表格示例:
  54. * {{list}} 用户名称 | 年龄 | 图片
  55. * [user] | [age] | [?img1][@#this][/img1]
  56. * list 为表格定义配置 需在配置文件配置,已提供默认表格配置 和 自定义配置方法 如一个文档中有 多处 需要循环表格 {{list}} 不可重复
  57. * 示例:
  58. * String avatar = "http://deepoove.com/images/icecream.png,http://deepoove.com/images/icecream.png,http://deepoove.com/images/icecream.png";
  59. * List<PictureRenderData> imgList = Arrays.stream(avatar.split(",")).map(s -> Pictures.ofStream(getInputStream(s))
  60. * .size(50, 50).create()).collect(Collectors.toList());
  61. * List<Map<String, Object>> par = new ArrayList<>();
  62. * for (int i = 0; i <= 1; i++) {
  63. * Map<String, Object> map1 = new HashMap<>(16);
  64. * map1.put("user", "测试一下吧");
  65. * map1.put("age", "123");
  66. * map1.put("img1", imgList);
  67. * par.add(map1);
  68. * }
  69. * map.put("list", par);
  70. * 其中 getInputStream(s) 方法是 获取url文件流方法,这里也可填入文件流 或 Pictures. 按自己需求来
  71. * <p>
  72. * 空白处循环添加图片:
  73. * {{?imga}}
  74. * {{@#this}}
  75. * {{#table}}
  76. * {{/imga}}
  77. * <p>
  78. * {{#table}} 占位符
  79. * <p>
  80. * 放入map方式如上
  81. * <p>
  82. * 支持 删除表格的 第一行 也就是表头 removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  83. *
  84. * @author: chenjiaxiang
  85. * @create: 2023/9/12 10:47
  86. **/
  87. @Slf4j
  88. public class PoiTlToWordUtils {
  89. /**
  90. * 导出文件到本地
  91. *
  92. * @param map 数据集合
  93. * @param nameList 循环增加表格的标识名称集合
  94. * @param filePath 模版地址
  95. * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx
  96. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  97. */
  98. public static void exportWordToLocality(Map<String, Object> map, List<String> nameList, String filePath, String outPath, List<String> removeTags) {
  99. exportFile(map, nameList, filePath, outPath, null, removeTags);
  100. }
  101. /**
  102. * 导出文件到本地,无循环表格
  103. *
  104. * @param map 数据集合
  105. * @param filePath 模版地址
  106. * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx
  107. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  108. */
  109. public static void exportWordToLocality(Map<String, Object> map, String filePath, String outPath, List<String> removeTags) {
  110. exportFile(map, null, filePath, outPath, null, removeTags);
  111. }
  112. /**
  113. * 导出文件到本地
  114. *
  115. * @param map 数据集合
  116. * @param nameList 循环增加表格的标识名称集合
  117. * @param filePath 模版地址
  118. * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx
  119. * @param configure 自定义配置文件
  120. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  121. */
  122. public static void exportWordToLocality(Map<String, Object> map, List<String> nameList, String filePath, String outPath, Configure configure, List<String> removeTags) {
  123. exportFile(map, nameList, filePath, outPath, configure, removeTags);
  124. }
  125. /**
  126. * 导出文件到本地,无循环表格
  127. *
  128. * @param map 数据集合
  129. * @param filePath 模版地址
  130. * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx
  131. * @param configure 自定义配置文件
  132. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  133. */
  134. public static void exportWordToLocality(Map<String, Object> map, String filePath, String outPath, Configure configure, List<String> removeTags) {
  135. exportFile(map, null, filePath, outPath, configure, removeTags);
  136. }
  137. /**
  138. * 浏览器下载
  139. *
  140. * @param response response
  141. * @param map 数据信息
  142. * @param nameList 循环增加表格的标识名称集合
  143. * @param filePath 模版文件地址
  144. * @param fileName 文件名称
  145. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  146. */
  147. public static void exportBrowser(HttpServletResponse response, Map<String, Object> map, List<String> nameList, String filePath, String fileName, List<String> removeTags) {
  148. exportFile(response, map, nameList, filePath, fileName, null, removeTags);
  149. }
  150. /**
  151. * 浏览器下载,无循环表格
  152. *
  153. * @param response response
  154. * @param map 数据信息
  155. * @param filePath 模版文件地址
  156. * @param fileName 文件名称
  157. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  158. */
  159. public static void exportBrowser(HttpServletResponse response, Map<String, Object> map, String filePath, String fileName, List<String> removeTags) {
  160. exportFile(response, map, null, filePath, fileName, null, removeTags);
  161. }
  162. /**
  163. * 浏览器下载 支持自定义配置文件
  164. *
  165. * @param response response
  166. * @param map 数据信息
  167. * @param nameList 循环增加表格的标识名称集合
  168. * @param filePath 模版文件地址
  169. * @param fileName 文件名称
  170. * @param configure 自定义配置文件
  171. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  172. */
  173. public static void exportBrowser(HttpServletResponse response, Map<String, Object> map, List<String> nameList, String filePath, String fileName, Configure configure, List<String> removeTags) {
  174. exportFile(response, map, nameList, filePath, fileName, configure, removeTags);
  175. }
  176. /**
  177. * 浏览器下载,无循环表格,支持自定义配置文件
  178. *
  179. * @param response response
  180. * @param map 数据信息
  181. * @param filePath 模版文件地址
  182. * @param fileName 文件名称
  183. * @param configure 自定义配置文件
  184. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  185. */
  186. public static void exportBrowser(HttpServletResponse response, Map<String, Object> map, String filePath, String fileName, Configure configure, List<String> removeTags) {
  187. exportFile(response, map, null, filePath, fileName, configure, removeTags);
  188. }
  189. /**
  190. * 文件生成返回 InputStream
  191. *
  192. * @param map 数据集合
  193. * @param nameList 循环增加表格的标识名称集合
  194. * @param filePath 模版地址
  195. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  196. */
  197. public static InputStream exportFileInputStream(Map<String, Object> map, List<String> nameList, String filePath, List<String> removeTags) {
  198. return exportFile(map, nameList, filePath, null, removeTags);
  199. }
  200. /**
  201. * 文件生成返回 InputStream ,无循环表格
  202. *
  203. * @param map 数据集合
  204. * @param filePath 模版地址
  205. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  206. */
  207. public static InputStream exportFileInputStream(Map<String, Object> map, String filePath, List<String> removeTags) {
  208. return exportFile(map, null, filePath, null, removeTags);
  209. }
  210. /**
  211. * 文件生成返回 InputStream
  212. *
  213. * @param map 数据集合
  214. * @param nameList 循环增加表格的标识名称集合
  215. * @param filePath 模版地址
  216. * @param configure 自定义配置文件
  217. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  218. */
  219. public static InputStream exportFileInputStream(Map<String, Object> map, List<String> nameList, String filePath, Configure configure, List<String> removeTags) {
  220. return exportFile(map, nameList, filePath, configure, removeTags);
  221. }
  222. /**
  223. * 文件生成返回 InputStream ,无循环表格
  224. *
  225. * @param map 数据集合
  226. * @param filePath 模版地址
  227. * @param configure 自定义配置文件
  228. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  229. */
  230. public static InputStream exportFileInputStream(Map<String, Object> map, String filePath, Configure configure, List<String> removeTags) {
  231. return exportFile(map, null, filePath, configure, removeTags);
  232. }
  233. /**
  234. * 导出文件到本地
  235. *
  236. * @param map 数据集合
  237. * @param nameList 循环增加表格的标识名称集合
  238. * @param filePath 模版地址
  239. * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx
  240. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  241. */
  242. private static void exportFile(Map<String, Object> map, List<String> nameList, String filePath, String outPath, Configure configure, List<String> removeTags) {
  243. XWPFTemplate template = null;
  244. BufferedOutputStream bos = null;
  245. OutputStream out = null;
  246. try {
  247. // 获取Word模板,模板存放路径在项目的resources目录下
  248. template = common(filePath, map, nameList, configure);
  249. out = new ByteArrayOutputStream();
  250. bos = new BufferedOutputStream(out);
  251. template.write(bos);
  252. bos.flush();
  253. out.flush();
  254. removeTags(template, removeTags);
  255. template.writeAndClose(Files.newOutputStream(Paths.get(outPath)));
  256. } catch (Exception e) {
  257. log.info("[生成word文件]-失败,{}", e.getMessage());
  258. throw new BusinessRuntimeException(e.getMessage());
  259. } finally {
  260. closeStream(template);
  261. closeStream(bos);
  262. closeStream(out);
  263. }
  264. }
  265. /**
  266. * 浏览器下载
  267. *
  268. * @param response response
  269. * @param map 数据信息
  270. * @param nameList 循环增加表格的标识名称集合
  271. * @param filePath 模版文件地址
  272. * @param fileName 文件名称
  273. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  274. */
  275. private static void exportFile(HttpServletResponse response, Map<String, Object> map, List<String> nameList, String filePath, String fileName, Configure configure, List<String> removeTags) {
  276. XWPFTemplate template = null;
  277. BufferedOutputStream bos = null;
  278. OutputStream out = null;
  279. try {
  280. // 获取Word模板,模板存放路径在项目的resources目录下
  281. template = common(filePath, map, nameList, configure);
  282. // 浏览器端下载 设置响应
  283. setResponse(response, fileName);
  284. out = response.getOutputStream();
  285. bos = new BufferedOutputStream(out);
  286. removeTags(template, removeTags);
  287. template.write(bos);
  288. bos.flush();
  289. out.flush();
  290. PoitlIOUtils.closeQuietlyMulti(template, bos, out);
  291. } catch (Exception e) {
  292. log.info("[生成word文件]-失败,{}", e.getMessage());
  293. throw new BusinessRuntimeException(e.getMessage());
  294. } finally {
  295. closeStream(template);
  296. closeStream(bos);
  297. closeStream(out);
  298. }
  299. }
  300. /**
  301. * 文件生成返回 InputStream
  302. *
  303. * @param map 数据集合
  304. * @param nameList 循环增加表格的标识名称集合
  305. * @param filePath 模版地址
  306. * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx
  307. */
  308. private static InputStream exportFile(Map<String, Object> map, List<String> nameList, String filePath, Configure configure, List<String> removeTags) {
  309. XWPFTemplate template = null;
  310. ByteArrayOutputStream bos = null;
  311. try {
  312. // 获取Word模板,模板存放路径在项目的resources目录下
  313. template = common(filePath, map, nameList, configure);
  314. bos = new ByteArrayOutputStream();
  315. removeTags(template, removeTags);
  316. template.write(bos);
  317. return new ByteArrayInputStream(bos.toByteArray());
  318. } catch (Exception e) {
  319. log.info("[生成word文件]-失败,{}", e.getMessage());
  320. throw new BusinessRuntimeException(e.getMessage());
  321. } finally {
  322. closeStream(template);
  323. closeStream(bos);
  324. }
  325. }
  326. private static XWPFTemplate common(String filePath, Map<String, Object> map, List<String> nameList, Configure configure) {
  327. //获取模版文件的文件流
  328. XWPFTemplate template;
  329. InputStream ins = null;
  330. try {
  331. ins = getTemplate(filePath);
  332. //如果有循环添加的数据表格的话 则绑定
  333. template = CollectionUtils.isNotEmpty(nameList) ? XWPFTemplate.compile(ins, Objects.nonNull(configure) ? configure : getConfig(nameList)).render(map)
  334. : XWPFTemplate.compile(ins).render(map);
  335. } finally {
  336. closeStream(ins);
  337. }
  338. return template;
  339. }
  340. private static <T extends Closeable> void closeStream(T ins) {
  341. if (ins != null) {
  342. try {
  343. ins.close();
  344. } catch (IOException e) {
  345. throw new RuntimeException(e);
  346. }
  347. }
  348. }
  349. /**
  350. * 绑定循环表格标识
  351. *
  352. * @param nameList 标识集合
  353. * @return 配置
  354. */
  355. private static Configure getConfig(List<String> nameList) {
  356. //使用行循环插件
  357. LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
  358. ConfigureBuilder builder = Configure.builder();
  359. nameList.forEach(name -> builder.bind(name, policy));
  360. return builder.build();
  361. }
  362. /**
  363. * 【获取网络文件的输入流】
  364. *
  365. * @param filePath: 网络文件路径
  366. * @return java.io.InputStream
  367. */
  368. public static InputStream getInputStream(String filePath) {
  369. InputStream inputStream = null;
  370. //创建URL
  371. try {
  372. URL url = new URL(filePath);
  373. //试图连接并取得返回状态码
  374. URLConnection urlconn = url.openConnection();
  375. urlconn.connect();
  376. HttpURLConnection httpconn = (HttpURLConnection) urlconn;
  377. int httpResult = httpconn.getResponseCode();
  378. if (httpResult == HttpURLConnection.HTTP_OK) {
  379. inputStream = urlconn.getInputStream();
  380. }
  381. } catch (IOException e) {
  382. throw new RuntimeException(e);
  383. }
  384. return inputStream;
  385. }
  386. /**
  387. * 获取模版文件流信息
  388. *
  389. * @param filePath 模版路径
  390. * @return
  391. */
  392. public static InputStream getTemplate(String filePath) {
  393. // 获取Word模板,模板存放路径在项目的resources目录下
  394. ClassPathResource classPathResource = new ClassPathResource(filePath);
  395. try {
  396. return classPathResource.getInputStream();
  397. } catch (IOException e) {
  398. throw new RuntimeException(e);
  399. }
  400. }
  401. /**
  402. * 放入 response
  403. *
  404. * @param response 响应
  405. * @param fileName 文件名称
  406. */
  407. private static void setResponse(HttpServletResponse response, String fileName) {
  408. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  409. response.setCharacterEncoding("utf-8");
  410. String filePoiName;
  411. try {
  412. filePoiName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
  413. } catch (UnsupportedEncodingException e) {
  414. throw new RuntimeException(e);
  415. }
  416. response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + filePoiName + ".docx");
  417. }
  418. /**
  419. * 去处表格标签
  420. *
  421. * @param template word
  422. * @param removeTags 标签集合
  423. */
  424. private static void removeTags(XWPFTemplate template, List<String> removeTags) {
  425. if (CollectionUtils.isNotEmpty(removeTags)) {
  426. traverseTables(template.getXWPFDocument(), removeTags);
  427. }
  428. }
  429. /**
  430. * 根据标签去除该表格的 第一行
  431. *
  432. * @param document 文件
  433. * @param removeTags 标签集合
  434. */
  435. private static void traverseTables(XWPFDocument document, List<String> removeTags) {
  436. List<XWPFTable> tables = document.getTables();
  437. // 递归处理嵌套表格
  438. tables.forEach(table -> {
  439. containsMarkerInFirstRow(table, removeTags);
  440. traverseNestedTables(table, removeTags);
  441. });
  442. }
  443. /**
  444. * 递归处理嵌套表格
  445. *
  446. * @param table 表格
  447. * @param removeTags 需要去处的表格标签
  448. */
  449. private static void traverseNestedTables(XWPFTable table, List<String> removeTags) {
  450. table.getRows().forEach(row -> row.getTableCells().forEach(cell -> traverseTables(cell, removeTags)));
  451. }
  452. /**
  453. * 传入cell 进行判断当前行是否包含
  454. */
  455. private static void traverseTables(XWPFTableCell cell, List<String> removeTags) {
  456. cell.getTables().forEach(table -> {
  457. // 判断是否包含标签 是的话则去除表格表头
  458. containsMarkerInFirstRow(table, removeTags);
  459. traverseNestedTables(table, removeTags);
  460. });
  461. }
  462. /**
  463. * 判断是否包含标签 是的话则去除表格表头
  464. *
  465. * @param table 表格
  466. * @param removeTags 需要去处的表格信息
  467. */
  468. private static void containsMarkerInFirstRow(XWPFTable table, List<String> removeTags) {
  469. List<XWPFTableRow> rows = table.getRows();
  470. int a = 0;
  471. if (!rows.isEmpty()) {
  472. for (XWPFTableRow row : rows) {
  473. if (removeTags.stream().anyMatch(removeTag -> row.getTable().getText().contains(String.format("{{%s}}", removeTag)) && rows.indexOf(row) == 0)) {
  474. table.removeRow(0);
  475. a = 1;
  476. }
  477. if (a > 0) {
  478. break;
  479. }
  480. }
  481. }
  482. }
  483. private static final String OUT_PATH = "/Users/chenjx/Downloads/zipceshi/0911/";
  484. private static final String FOP = "/file/elTest123.docx";
  485. public static void main(String[] args) {
  486. Map<String, Object> m = new HashMap<>();
  487. m.put("baseName", "爱就是打卡上");
  488. m.put("imgUrl", Pictures.ofStream(getInputStream("http://img.crcz.com/allimg/202003/27/1585280268112506.jpg"))
  489. .size(50, 50).create());
  490. //try {
  491. String avatar = "http://img.crcz.com/allimg/202003/27/1585280268105939.jpg," +
  492. "http://img.crcz.com/allimg/202003/27/1585280268458675.jpg," +
  493. "http://img.crcz.com/allimg/202003/26/1585192258743890-lp.jpg";
  494. List<PictureRenderData> imgList = Arrays.stream(avatar.split(",")).map(s -> Pictures.ofStream(getInputStream(s))
  495. .size(50, 50).create()).collect(Collectors.toList());
  496. List<Map<String, Object>> par = new ArrayList<>();
  497. for (int i = 0; i <= 1; i++) {
  498. Map<String, Object> map = new HashMap<>(16);
  499. map.put("user", "测试一下吧");
  500. map.put("pag", "123");
  501. map.put("img1", imgList);
  502. par.add(map);
  503. }
  504. m.put("imgs", imgList);
  505. m.put("img", imgList);
  506. m.put("list", par);
  507. m.put("list1", par);
  508. m.put("list3", par);
  509. m.put("imga", imgList);
  510. m.put("mo", new HyperlinkTextRenderData("测试超链接", "http://www.baidu.com"));
  511. List<Map<String, Object>> par1 = new ArrayList<>();
  512. for (int i = 0; i <= 2; i++) {
  513. Map<String, Object> map = new LinkedHashMap<>(16);
  514. map.put("test1", "测试一下吧");
  515. map.put("test2", "123");
  516. map.put("test3", imgList);
  517. par1.add(map);
  518. }
  519. exportWordToLocality(m, Arrays.asList("list", "list1", "list3"), FOP, OUT_PATH + UUID.randomUUID() + ".docx", Arrays.asList("remove_tit", "remove_tit1"));
  520. }
  521. }

生成效果:

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

闽ICP备14008679号