Java根据word 模版生成 word文件,支持 循环多图片及表格多图片 支持按照标记去除表格第一行表头
模版文件

jar包
com.deepoove
poi-tl
1.12.1
可能会出现poi jar包冲突 如出现 将老版本的poi排掉就好 ,也可能出现 log4j 版本低 用下面这个就行
org.apache.logging.log4j
log4j-api
2.17.1
代码
package com.util; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.config.ConfigureBuilder; import com.deepoove.poi.data.HyperlinkTextRenderData; import com.deepoove.poi.data.PictureRenderData; import com.deepoove.poi.data.Pictures; import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; import com.deepoove.poi.util.PoitlIOUtils; import com.zjjw.platform.core.exception.BusinessRuntimeException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.springframework.core.io.ClassPathResource; import javax.servlet.http.HttpServletResponse; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; /** * POI-TL 进行转换 支持多图片及循环增加表格 ** 使用方式: * 参数传入类型 Map * 模版 纯文本 为: * {{name}} 则map.put("name",params) name 是自己定义的名字 下面的都是自己定义名字,只是示例 *
* 图片为: * {{@imgUrl}} 只要是 图片的 都需在参数前 加 @ 符号 这个只支持单张图片!!! map.put("imgUrl",params) *
* 循环表格:表格示例: * {{list}} 用户名称 | 年龄 | 图片 * [user] | [age] | [?img1][@#this][/img1] * list 为表格定义配置 需在配置文件配置,已提供默认表格配置 和 自定义配置方法 如一个文档中有 多处 需要循环表格 {{list}} 不可重复 * 示例: * String avatar = "http://deepoove.com/images/icecream.png,http://deepoove.com/images/icecream.png,http://deepoove.com/images/icecream.png"; * List imgList = Arrays.stream(avatar.split(",")).map(s -> Pictures.ofStream(getInputStream(s)) * .size(50, 50).create()).collect(Collectors.toList()); * List<Map> par = new ArrayList(); * for (int i = 0; i <= 1; i++) { * Map map1 = new HashMap(16); * map1.put("user", "测试一下吧"); * map1.put("age", "123"); * map1.put("img1", imgList); * par.add(map1); * } * map.put("list", par); * 其中 getInputStream(s) 方法是 获取url文件流方法,这里也可填入文件流 或 Pictures. 按自己需求来 *
* 空白处循环添加图片: * {{?imga}} * {{@#this}} * {{#table}} * {{/imga}} *
* {{#table}} 占位符 *
* 放入map方式如上 *
* 支持 删除表格的 第一行 也就是表头 removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx * * @author: chenjiaxiang * @create: 2023/9/12 10:47 **/@Slf4jpublic class PoiTlToWordUtils { /** * 导出文件到本地 * * @param map 数据集合 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版地址 * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportWordToLocality(Map map, List nameList, String filePath, String outPath, List removeTags) { exportFile(map, nameList, filePath, outPath, null, removeTags); } /** * 导出文件到本地,无循环表格 * * @param map 数据集合 * @param filePath 模版地址 * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportWordToLocality(Map map, String filePath, String outPath, List removeTags) { exportFile(map, null, filePath, outPath, null, removeTags); } /** * 导出文件到本地 * * @param map 数据集合 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版地址 * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx * @param configure 自定义配置文件 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportWordToLocality(Map map, List nameList, String filePath, String outPath, Configure configure, List removeTags) { exportFile(map, nameList, filePath, outPath, configure, removeTags); } /** * 导出文件到本地,无循环表格 * * @param map 数据集合 * @param filePath 模版地址 * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx * @param configure 自定义配置文件 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportWordToLocality(Map map, String filePath, String outPath, Configure configure, List removeTags) { exportFile(map, null, filePath, outPath, configure, removeTags); } /** * 浏览器下载 * * @param response response * @param map 数据信息 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版文件地址 * @param fileName 文件名称 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportBrowser(HttpServletResponse response, Map map, List nameList, String filePath, String fileName, List removeTags) { exportFile(response, map, nameList, filePath, fileName, null, removeTags); } /** * 浏览器下载,无循环表格 * * @param response response * @param map 数据信息 * @param filePath 模版文件地址 * @param fileName 文件名称 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportBrowser(HttpServletResponse response, Map map, String filePath, String fileName, List removeTags) { exportFile(response, map, null, filePath, fileName, null, removeTags); } /** * 浏览器下载 支持自定义配置文件 * * @param response response * @param map 数据信息 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版文件地址 * @param fileName 文件名称 * @param configure 自定义配置文件 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportBrowser(HttpServletResponse response, Map map, List nameList, String filePath, String fileName, Configure configure, List removeTags) { exportFile(response, map, nameList, filePath, fileName, configure, removeTags); } /** * 浏览器下载,无循环表格,支持自定义配置文件 * * @param response response * @param map 数据信息 * @param filePath 模版文件地址 * @param fileName 文件名称 * @param configure 自定义配置文件 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static void exportBrowser(HttpServletResponse response, Map map, String filePath, String fileName, Configure configure, List removeTags) { exportFile(response, map, null, filePath, fileName, configure, removeTags); } /** * 文件生成返回 InputStream * * @param map 数据集合 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版地址 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static InputStream exportFileInputStream(Map map, List nameList, String filePath, List removeTags) { return exportFile(map, nameList, filePath, null, removeTags); } /** * 文件生成返回 InputStream ,无循环表格 * * @param map 数据集合 * @param filePath 模版地址 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static InputStream exportFileInputStream(Map map, String filePath, List removeTags) { return exportFile(map, null, filePath, null, removeTags); } /** * 文件生成返回 InputStream * * @param map 数据集合 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版地址 * @param configure 自定义配置文件 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static InputStream exportFileInputStream(Map map, List nameList, String filePath, Configure configure, List removeTags) { return exportFile(map, nameList, filePath, configure, removeTags); } /** * 文件生成返回 InputStream ,无循环表格 * * @param map 数据集合 * @param filePath 模版地址 * @param configure 自定义配置文件 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ public static InputStream exportFileInputStream(Map map, String filePath, Configure configure, List removeTags) { return exportFile(map, null, filePath, configure, removeTags); } /** * 导出文件到本地 * * @param map 数据集合 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版地址 * @param outPath 输出地址及文件名称 需要后缀 ,如 /xx/xx/xx.docx * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ private static void exportFile(Map map, List nameList, String filePath, String outPath, Configure configure, List removeTags) { XWPFTemplate template = null; BufferedOutputStream bos = null; OutputStream out = null; try { // 获取Word模板,模板存放路径在项目的resources目录下 template = common(filePath, map, nameList, configure); out = new ByteArrayOutputStream(); bos = new BufferedOutputStream(out); template.write(bos); bos.flush(); out.flush(); removeTags(template, removeTags); template.writeAndClose(Files.newOutputStream(Paths.get(outPath))); } catch (Exception e) { log.info("[生成word文件]-失败,{}", e.getMessage()); throw new BusinessRuntimeException(e.getMessage()); } finally { closeStream(template); closeStream(bos); closeStream(out); } } /** * 浏览器下载 * * @param response response * @param map 数据信息 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版文件地址 * @param fileName 文件名称 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ private static void exportFile(HttpServletResponse response, Map map, List nameList, String filePath, String fileName, Configure configure, List removeTags) { XWPFTemplate template = null; BufferedOutputStream bos = null; OutputStream out = null; try { // 获取Word模板,模板存放路径在项目的resources目录下 template = common(filePath, map, nameList, configure); // 浏览器端下载 设置响应 setResponse(response, fileName); out = response.getOutputStream(); bos = new BufferedOutputStream(out); removeTags(template, removeTags); template.write(bos); bos.flush(); out.flush(); PoitlIOUtils.closeQuietlyMulti(template, bos, out); } catch (Exception e) { log.info("[生成word文件]-失败,{}", e.getMessage()); throw new BusinessRuntimeException(e.getMessage()); } finally { closeStream(template); closeStream(bos); closeStream(out); } } /** * 文件生成返回 InputStream * * @param map 数据集合 * @param nameList 循环增加表格的标识名称集合 * @param filePath 模版地址 * @param removeTags 需要去处的表格第一行的标签 word中定义为 {{xxx}} 集合中传入 xxx */ private static InputStream exportFile(Map map, List nameList, String filePath, Configure configure, List removeTags) { XWPFTemplate template = null; ByteArrayOutputStream bos = null; try { // 获取Word模板,模板存放路径在项目的resources目录下 template = common(filePath, map, nameList, configure); bos = new ByteArrayOutputStream(); removeTags(template, removeTags); template.write(bos); return new ByteArrayInputStream(bos.toByteArray()); } catch (Exception e) { log.info("[生成word文件]-失败,{}", e.getMessage()); throw new BusinessRuntimeException(e.getMessage()); } finally { closeStream(template); closeStream(bos); } } private static XWPFTemplate common(String filePath, Map map, List nameList, Configure configure) { //获取模版文件的文件流 XWPFTemplate template; InputStream ins = null; try { ins = getTemplate(filePath); //如果有循环添加的数据表格的话 则绑定 template = CollectionUtils.isNotEmpty(nameList) ? XWPFTemplate.compile(ins, Objects.nonNull(configure) ? configure : getConfig(nameList)).render(map) : XWPFTemplate.compile(ins).render(map); } finally { closeStream(ins); } return template; } private static void closeStream(T ins) { if (ins != null) { try { ins.close(); } catch (IOException e) { throw new RuntimeException(e); } } } /** * 绑定循环表格标识 * * @param nameList 标识集合 * @return 配置 */ private static Configure getConfig(List nameList) { //使用行循环插件 LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); ConfigureBuilder builder = Configure.builder(); nameList.forEach(name -> builder.bind(name, policy)); return builder.build(); } /** * 【获取网络文件的输入流】 * * @param filePath: 网络文件路径 * @return java.io.InputStream */ public static InputStream getInputStream(String filePath) { InputStream inputStream = null; //创建URL try { URL url = new URL(filePath); //试图连接并取得返回状态码 URLConnection urlconn = url.openConnection(); urlconn.connect(); HttpURLConnection httpconn = (HttpURLConnection) urlconn; int httpResult = httpconn.getResponseCode(); if (httpResult == HttpURLConnection.HTTP_OK) { inputStream = urlconn.getInputStream(); } } catch (IOException e) { throw new RuntimeException(e); } return inputStream; } /** * 获取模版文件流信息 * * @param filePath 模版路径 * @return 流 */ public static InputStream getTemplate(String filePath) { // 获取Word模板,模板存放路径在项目的resources目录下 ClassPathResource classPathResource = new ClassPathResource(filePath); try { return classPathResource.getInputStream(); } catch (IOException e) { throw new RuntimeException(e); } } /** * 放入 response * * @param response 响应 * @param fileName 文件名称 */ private static void setResponse(HttpServletResponse response, String fileName) { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String filePoiName; try { filePoiName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + filePoiName + ".docx"); } /** * 去处表格标签 * * @param template word * @param removeTags 标签集合 */ private static void removeTags(XWPFTemplate template, List removeTags) { if (CollectionUtils.isNotEmpty(removeTags)) { traverseTables(template.getXWPFDocument(), removeTags); } } /** * 根据标签去除该表格的 第一行 * * @param document 文件 * @param removeTags 标签集合 */ private static void traverseTables(XWPFDocument document, List removeTags) { List tables = document.getTables(); // 递归处理嵌套表格 tables.forEach(table -> { containsMarkerInFirstRow(table, removeTags); traverseNestedTables(table, removeTags); }); } /** * 递归处理嵌套表格 * * @param table 表格 * @param removeTags 需要去处的表格标签 */ private static void traverseNestedTables(XWPFTable table, List removeTags) { table.getRows().forEach(row -> row.getTableCells().forEach(cell -> traverseTables(cell, removeTags))); } /** * 传入cell 进行判断当前行是否包含 */ private static void traverseTables(XWPFTableCell cell, List removeTags) { cell.getTables().forEach(table -> { // 判断是否包含标签 是的话则去除表格表头 containsMarkerInFirstRow(table, removeTags); traverseNestedTables(table, removeTags); }); } /** * 判断是否包含标签 是的话则去除表格表头 * * @param table 表格 * @param removeTags 需要去处的表格信息 */ private static void containsMarkerInFirstRow(XWPFTable table, List removeTags) { List rows = table.getRows(); int a = 0; if (!rows.isEmpty()) { for (XWPFTableRow row : rows) { if (removeTags.stream().anyMatch(removeTag -> row.getTable().getText().contains(String.format("{{%s}}", removeTag)) && rows.indexOf(row) == 0)) { table.removeRow(0); a = 1; } if (a > 0) { break; } } } } private static final String OUT_PATH = "/Users/chenjx/Downloads/zipceshi/0911/"; private static final String FOP = "/file/elTest123.docx"; public static void main(String[] args) { Map m = new HashMap(); m.put("baseName", "爱就是打卡上"); m.put("imgUrl", Pictures.ofStream(getInputStream("http://img.crcz.com/allimg/202003/27/1585280268112506.jpg")) .size(50, 50).create()); //try { String avatar = "http://img.crcz.com/allimg/202003/27/1585280268105939.jpg," + "http://img.crcz.com/allimg/202003/27/1585280268458675.jpg," + "http://img.crcz.com/allimg/202003/26/1585192258743890-lp.jpg"; List imgList = Arrays.stream(avatar.split(",")).map(s -> Pictures.ofStream(getInputStream(s)) .size(50, 50).create()).collect(Collectors.toList()); List<Map> par = new ArrayList(); for (int i = 0; i <= 1; i++) { Map map = new HashMap(16); map.put("user", "测试一下吧"); map.put("pag", "123"); map.put("img1", imgList); par.add(map); } m.put("imgs", imgList); m.put("img", imgList); m.put("list", par); m.put("list1", par); m.put("list3", par); m.put("imga", imgList); m.put("mo", new HyperlinkTextRenderData("测试超链接", "http://www.baidu.com")); List<Map> par1 = new ArrayList(); for (int i = 0; i <= 2; i++) { Map map = new LinkedHashMap(16); map.put("test1", "测试一下吧"); map.put("test2", "123"); map.put("test3", imgList); par1.add(map); } exportWordToLocality(m, Arrays.asList("list", "list1", "list3"), FOP, OUT_PATH + UUID.randomUUID() + ".docx", Arrays.asList("remove_tit", "remove_tit1")); }}
生成效果:

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/a21420f0f4.html
