全文介绍
poi-tl(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档 。
常用标签介绍(官网):
1文本:{{var}} 2. 图片:{{@var}}
3表格:{{#var}} 4. 列表:{{*var}}
5区块对:{{?sections}}{{/sections}} 6. 嵌套:{{+var}}
本次制作的Demo,简写代码、多注释,只为容易理解,读者可根据自己需求进行重构代码、优化代码。
1根据{{table}}、[content],实现word表格导出(有模板)
2根据{{?sections}}{{/sections}},多个表格段落进行表格、图片插入
环境配置
<!-- poi-tl --> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.10.0</version> </dependency> <!-- 导出图片指定html格式 --> <dependency> <groupId>io.github.draco1023</groupId> <artifactId>poi-tl-ext</artifactId> <version>0.4.2</version> </dependency>
1根据{{table}}、[content],实现word表格导出(有模板)
1.1模板
模板在代码中位置
1.2代码实现
//测试类实现
public class ExportWordTest7 { @Test void exportWord() throws IOException { //1 获取模板文件流 InputStream resourceAsStream = this.getClass().getResourceAsStream("/templates/pmwordBg.docx"); //2 word数据集合 Map<String, Object> params = new HashMap<>(); params.put("workContentss",getDynamicFlag1()); //3 图片指定插件 ConfigureBuilder builder = Configure.builder(); Configure config = builder.build(); HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy(); config.customPolicy("workContentss", policy); //4 word导出 //本地测试 String filePath = FileUtil.getProjectPath() + "document" + File.separator + "牧羊人导出实例.docx"; dynamicExport(params,new File(filePath),resourceAsStream,config,null,false); //浏览器文件名称设置 // String fileName = wordExportPmProtermVo.getProjectName()+"第"+wordExportPmProtermVo.getJournalTerm()+"期" + ".docx"; } /** * @param data 填充数据 * @param filePath 临时路径 * @param resource 模板路径 * @param response 通过浏览器下载需要 * @param isBrowser true-通过浏览器下载 false-下载到临时路径 */ public void dynamicExport(Map<String, Object> data, File filePath, InputStream resource, Configure config, HttpServletResponse response, boolean isBrowser){ try { if (Boolean.FALSE.equals(isBrowser)) {//本地 File parentFile = filePath.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } FileOutputStream out = new FileOutputStream(filePath); XWPFTemplate.compile(resource, config).render(data).writeAndClose(out); } else {//浏览器 ServletOutputStream out = response.getOutputStream(); XWPFTemplate.compile(resource, config).render(data).writeAndClose(out); } }catch (Exception e) { e.printStackTrace(); } } /** * @Description: 一、本日主要工作数据整理 * @Author: syq * @Date: 2023/4/6 18:31 */ public List<Map<String,Object>> getDynamicFlag1(){ List<Map<String, Object>> dynamicFlag = new ArrayList<>(); for (int i = 0; i < 4; i++) { Map<String, Object> dynamicTableMap = new HashMap<>(); dynamicTableMap.put("xh", i+1);//序号 dynamicTableMap.put("workContent", "工作内容");//专业主要工作 dynamicTableMap.put("professional", "专业");//专业 dynamicTableMap.put("inspectedBy", "检查人");//检查人 dynamicFlag.add(dynamicTableMap); } return dynamicFlag; } }
/** * @author: muyangren * @Date: 2023/1/14 * @Description: com.muyangren.utils * @Version: 1.0 */ public class FileUtil { /** * 创建FileItem * @param file * @param fieldName * @return */ public static MultipartFile createFileItem(File file, String fieldName) { FileItemFactory factory = new DiskFileItemFactory(16, null); FileItem item = factory.createItem(fieldName, ContentType.MULTIPART_FORM_DATA.toString(), true, file.getName()); int bytesRead = 0; byte[] buffer = new byte[8192]; try { FileInputStream fis = new FileInputStream(file); OutputStream os = item.getOutputStream(); while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); fis.close(); } catch (IOException e) { e.printStackTrace(); } return new CommonsMultipartFile(item); } /** * 下载到本地路径 * @param file * @return * @throws IOException */ public static File fileDownloadToLocalPath(MultipartFile file) { File destFile = null; try { //获取文件名称 if (StringUtils.isEmpty(file.getOriginalFilename())){ throw new ServerException("导入模板失败!"); } String fileName = file.getOriginalFilename(); //获取文件后缀 String pref = fileName.lastIndexOf(".") != -1 ? fileName.substring(fileName.lastIndexOf(".") + 1) : null; //临时文件 //临时文件名避免重复 String uuidFile = UUID.randomUUID().toString().replace("-", "") + "." + pref; destFile = new File(FileUtil.getProjectPath() + uuidFile); if (!destFile.getParentFile().exists()) { destFile.getParentFile().mkdirs(); } file.transferTo(destFile); } catch (IOException e) { e.printStackTrace(); } return destFile; } /** * @return 文件路径 */ public static String getProjectPath(){ String os = System.getProperty("os.name").toLowerCase(); //windows下 if (os.indexOf("windows")>=0) { return "C://temp/"; }else{ return "/usr/local/temp/"; } }
1.3效果图
2根据{{?sections}}{{/sections}},多个表格段落进行表格、图片插入
2.1模板
2.2代码实现
package com.muyangren; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.config.ConfigureBuilder; import com.muyangren.enums.StyleEnum; import com.muyangren.utils.FileUtil; import com.muyangren.utils.HtmlUtil; import com.muyangren.vo.Attachment; import com.muyangren.vo.Attachments; import org.apache.commons.lang3.StringUtils; import org.ddr.poi.html.HtmlRenderPolicy; import org.junit.jupiter.api.Test; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.*; /** * @Author: syq * @Description: ${description} * @Date: 2023/4/9 14:33 * @Version: 1.0 */ public class ExportWordTest8 { /** * @Description: word按照指定模式进行导出 * 1 获取模板文件流 * 2 数据与word锚点匹配 * 3 图片指定插件 * 4 word导出 * @Author: syq * @Date: 2023/4/6 17:22 */ @Test void exportWord() throws IOException { //1 获取模板文件流 InputStream resourceAsStream = this.getClass().getResourceAsStream("/templates/pmwordtest.docx"); //2 word数据集合 Map<String, Object> params = new HashMap<>(); //2.3 数据与word锚点匹配 params.put("dynamicFlag1",getDynamicFlag1()); params.put("workTitle","工作概述"); //3 图片指定插件 HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy(); ConfigureBuilder builder = Configure.builder(); Configure config = builder.build(); config.customPolicy("pictureUrl", htmlRenderPolicy); //4 word导出 //本地测试 String filePath = FileUtil.getProjectPath() + "document" + File.separator + "牧羊人导出实例.docx"; dynamicExport(params,new File(filePath),resourceAsStream,config,null,false); //浏览器文件名称设置 // String fileName = wordExportPmProtermVo.getProjectName()+"第"+wordExportPmProtermVo.getJournalTerm()+"期" + ".docx"; // try { // response.setHeader("Content-disposition", "attachment;filename=".concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8")))); // } catch (UnsupportedEncodingException e) { // e.printStackTrace(); // } } /** * @param data 填充数据 * @param filePath 临时路径 * @param resource 模板路径 * @param response 通过浏览器下载需要 * @param isBrowser true-通过浏览器下载 false-下载到临时路径 */ public void dynamicExport(Map<String, Object> data, File filePath, InputStream resource, Configure config, HttpServletResponse response, boolean isBrowser){ try { if (Boolean.FALSE.equals(isBrowser)) {//本地 File parentFile = filePath.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } FileOutputStream out = new FileOutputStream(filePath); XWPFTemplate.compile(resource, config).render(data).writeAndClose(out); } else {//浏览器 ServletOutputStream out = response.getOutputStream(); XWPFTemplate.compile(resource, config).render(data).writeAndClose(out); } }catch (Exception e) { e.printStackTrace(); } } /** * @Description: 一、本日主要工作数据整理 * @Author: syq * @Date: 2023/4/6 18:31 */ public List<Map<String,Object>> getDynamicFlag1() throws IOException { List<Map<String, Object>> dynamicFlag = new ArrayList<>(); for (int i = 0; i < 4; i++) { Map<String, Object> dynamicTableMap = new HashMap<>(); dynamicTableMap.put("xh", i+1);//序号 dynamicTableMap.put("workContent", "工作内容");//专业主要工作 dynamicTableMap.put("professional", "专业");//专业 dynamicTableMap.put("inspectedBy", "检查人");//检查人 //url方式 // List<String> list = new ArrayList<>(); // list.add("https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87&hs=0&pn=0&spn=0&di=7207123747399008257&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=1204793430%2C3263400171&os=3423103612%2C2074051226&simid=1204793430%2C3263400171&adpicid=0&lpn=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=0&oriquery=%E5%9B%BE%E7%89%87&objurl=https%3A%2F%2Fup.deskcity.org%2Fpic_source%2F2f%2Ff4%2F42%2F2ff442798331f6cc6005098766304e39.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B1jfhvtpy_z%26e3B562AzdH3F15ogs5w1AzdH3Fdd9m0m-a-axa_z%26e3Bip4s&gsm=&islist=&querylist=&dyTabStr=MCwxLDYsMyw0LDUsMiw3LDgsOQ%3D%3D"); // dynamicTableMap.put("pictureUrl",dealWithPictureWidthAndHeight(getBase64String(list))); //本地方式 File file = new File("C:/aa.png"); String baseString = base64String(file); baseString = "data:image/jpeg;base64," + baseString; String base = "<img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>" ; // "<img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>"+ // "<img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>"; // String base = "<p style=\"white-space:pre-wrap;\"><img src=" + baseString + " style=\"width:194.1pt;height:126.05pt;\"/>"; dynamicTableMap.put("pictureUrl", dealWithPictureWidthAndHeight(base));//一张或多张图片 dynamicFlag.add(dynamicTableMap); } return dynamicFlag; } public String base64String(File file) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); FileInputStream fileInputStream = new FileInputStream(file); byte[] bytes = new byte[1024]; //用来定义一个准备接收图片总长度的局部变量 int len; //将流的内容读取到bytes中 while ((len = fileInputStream.read(bytes)) > 0) { //将bytes内存中的内容从0开始到总长度输出出去 out.write(bytes, 0, len); } //通过util包中的Base64类对字节数组进行base64编码 return Base64.getEncoder().encodeToString(out.toByteArray()); } /** * @Description: 根据url获取图片base64 * @Author: syq * @Date: 2023/4/7 16:37 */ public String getBase64String(List<String> listPath){ String base64String = ""; for (int i = 0; i < listPath.size(); i++) { String address = listPath.get(i); if(StringUtils.isNotBlank(address)){ String get = netSourceToBase64(address, "GET"); if(StringUtils.isNotBlank(get)){ get = "data:image/jpeg;base64," + get; base64String = base64String + "<img src=" + get + " style=\"width:194.1pt;height:126.05pt;\"/>"; } } } return base64String; } public static String netSourceToBase64(String srcUrl, String requestMethod) { ByteArrayOutputStream outPut = new ByteArrayOutputStream(); byte[] data = new byte[1024 * 8]; try { // 创建URL URL url = new URL(srcUrl); // 创建链接 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod(requestMethod); conn.setConnectTimeout(10 * 1000); if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { //连接失败/链接失效/文件不存在 return null; } InputStream inStream = conn.getInputStream(); int len = -1; while (-1 != (len = inStream.read(data))) { outPut.write(data, 0, len); } inStream.close(); } catch (IOException e) { e.printStackTrace(); } // 对字节数组Base64编码 return Base64.getEncoder().encodeToString(outPut.toByteArray()); } /** * @Description: 图片处理 * @Author: syq * @Date: 2023/4/7 16:04 */ private String dealWithPictureWidthAndHeight(String content) { List<HashMap<String, String>> imagesFiles = HtmlUtil.regexMatchWidthAndHeight(content); if (imagesFiles.size() > 0) { for (HashMap<String, String> imagesFile : imagesFiles) { String newFileUrl = imagesFile.get(StyleEnum.NEW_FILE_URL.getValue()); String fileUrl = imagesFile.get(StyleEnum.FILE_URL.getValue()); if (newFileUrl != null) { content = content.replace(fileUrl, newFileUrl); } } } return content; } }