irpas技术客

SpringBoot + Echars + Thymeleaf 后端转html,pdf_springboot 获取 tyhmeleaf 生成html_老鼠扛刀满

大大的周 5236

文章目录 SpringBoot + Echars + Thymeleaf 后端转html,pdf1. 需要引用的依赖2. 后端将echars报表生成jpg2.1 phantomjs及echarts-convert资料下载2.1.1 phantomjs介绍2.1.2 echarts-convert.js 2.2 echars转image.png2.2.1 EchartsFileService 调用服务2.2.2 EchartsUtil 工具生成图片2.2.3 application.propertyies 3. Thymeleaf 模板生成html,pdf3.1.1 ConstantConfigurations 相关配置信息3.1.2 ThymeleafService 转html pdf3.1.3 ImgBase64Util3.1.4 PdfBase64ImgReplacedElementFactory html转pdf图片处理自定义工厂3.1.7 EcharTemplate.html 模板3.1.6 application.properties 4. 测试接口5 补充

SpringBoot + Echars + Thymeleaf 后端转html,pdf

需求: 后端定期跑批,生成pdf文件发送给客户。

版本: SpringBoot:2.1.4.RELEASE

思路

后端将echars报表生成jpg,png图片通过Thymeleaf 模板生成html将html转成pdf文件 1. 需要引用的依赖 <!-- thymeleaf模板 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- html 转 pdf 需要用的jar --> <dependency> <groupId>org.xhtmlrenderer</groupId>[添加链接描述](https://pan.baidu.com/s/1lvvdfPT1-Q9PENHq4HO5Ow) <artifactId>flying-saucer-pdf</artifactId> <version>9.1.6</version> </dependency> 2. 后端将echars报表生成jpg 2.1 phantomjs及echarts-convert资料下载

https://pan.baidu.com/s/1lvvdfPT1-Q9PENHq4HO5Ow 提取码:dq3z

2.1.1 phantomjs介绍

可以理解为一个插件,后端听过指令调用该插件将echars报表生成图片。

2.1.2 echarts-convert.js

echars相关js脚本,phantomjs执行需要的引用依赖。

2.2 echars转image.png 2.2.1 EchartsFileService 调用服务 @Service public class EchartsFileServiceImpl implements EchartsFileService { public static final Logger logger = LoggerFactory.getLogger(EchartsFileServiceImpl.class); @Autowired private EchartsUtil echartsUtil; /** * @param options json echar图标数据 * @return */ @Override public String creatEchartsFile(String options) { try { JSON.parseObject(options); } catch (Exception e) { logger.error("options is not jsonString"); return null; } return echartsUtil.generateEChart(options); } } 2.2.2 EchartsUtil 工具生成图片 @Component public class EchartsUtil { public static final Logger logger = LoggerFactory.getLogger(EchartsUtil.class); // phantomjs执行器目录: @Value("${echars.phantomJs}") private String phantomJs; // 图片生成的临时文件目录 @Value("${echars.fileDirectory}") private String fileDirectory; @Value("${echars.jsPath}") private String jsPath; // 指令: 执行器路径 echarts-convert.js路径 -infile 数据源 -outfile 输出文件 private final String EXEC_CMD = "{0} {1} -infile {2} -outfile {3}"; /** * @param options 报表数据源 * @return 生成文件地址 返回图片生成的路径 */ public String generateEChart(String options) { // 随机生成文件名 String wFileName = UUID.randomUUID().toString(); String imageName = wFileName + ".png"; String jsonPath = writeFile(options, wFileName); // 生成的文件存储路径 String imagePath = fileDirectory + imageName; try { File file = new File(jsonPath); //文件路径 if (!file.exists()) { File dir = new File(file.getParent()); dir.mkdirs(); file.createNewFile(); } String cmd = MessageFormat.format(EXEC_CMD, phantomJs, jsPath, jsonPath, imagePath); Runtime.getRuntime().exec(cmd); } catch (IOException e) { e.printStackTrace(); logger.error("error: {}", e); } logger.info("echars file path: {}", imagePath); return imagePath; } /* * options:json数据 * wFileName:文件输出路径 * 生成phantomjs生成图片的数据文件 * return .json文件全路径 */ public String writeFile(String options, String wFileName) { /* option写入文本文件 用于执行命令*/ String wjsonPath = fileDirectory + wFileName + ".json"; BufferedWriter out = null; try { File jsonFile = new File(wjsonPath); if (!jsonFile.exists()) { File dir = new File(jsonFile.getParent()); dir.mkdirs(); jsonFile.createNewFile(); } out = new BufferedWriter(new FileWriter(jsonFile)); out.write(options); out.flush(); // 把缓存区内容压入文件 } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); // 最后关闭文件 } } catch (IOException e) { e.printStackTrace(); } } return wjsonPath; } } 2.2.3 application.propertyies ## echars图片生成相关插件路径配置 # phantomJs 执行路径 zifisense.echars.phantomJs=xxx\\phantomjs\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe # echar生成文件目录后最\\或者是/不可缺少 zifisense.echars.fileDirectory=C:\\Users\\Desktop\\model\\ # echarts-convert.js文件路径 zifisense.echars.jsPath=xxx\\echarts-convert\\echarts-convert.js 3. Thymeleaf 模板生成html,pdf 3.1.1 ConstantConfigurations 相关配置信息 /** * Thymeleaf配置参数 参数配置化对象 */ @Configuration @ConfigurationProperties( prefix = "constant" ) @Data public class ConstantConfigurations { // html文件生成默认路径 private String indexStorage; // 指定模板解析器模板文件路径前缀,根路径resources private String resolverPrefix = "/config/templates/model/"; // 指定模板解析器模板文件后缀,根路径resources private String resolverSuffix = ".html"; } 3.1.2 ThymeleafService 转html pdf @Service public class ThymeleafServiceImpl implements ThymeleafService { public static final Logger logger = LoggerFactory.getLogger(ThymeleafServiceImpl.class); /** * sifisense 参数配置化对象 */ @Autowired private ConstantConfigurations ConstantConfigurations ; /** * 模板引擎 */ @Autowired private TemplateEngine templateEngine; /** * 创建出一个name.html文件 * * @param templateName 模板名称 * @param name 生成name.xml * @param map 模板映射参数 */ @Override public String createHtml(String templateName, String name, Map<String, Object> map) { PrintWriter writer = null; try { SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); // 1. 创建模板解析目录解析器 Set<ITemplateResolver> templateResolvers = templateEngine.getTemplateResolvers(); // 无配置模板解析路径,则代码配置 if(!templateResolvers.iterator().hasNext()) { // 2. 创建模板解析器 并设置相关属性 resolver.setPrefix(ConstantConfigurations .getResolverPrefix()); resolver.setSuffix(ConstantConfigurations .getResolverSuffix()); // 不允许重复设置 否则会报错 templateEngine.setTemplateResolver(resolver); } // 2. 模板上下文 主要存储Model参数 Context context = new Context(); if (map.size() > 0) { context.setVariables(map); } // 3. 创建输出文件 File folder = new File(ConstantConfigurations .getIndexStorage(), name + ".html"); //如果文件不存在,直接创建 if (!folder.exists()) { folder.createNewFile(); } // 5. 获取输出目标文件输出流 writer = new PrintWriter(folder, "UTF-8"); // 6. 生成静态模板参数1:template模板名称 参数2:上下文对象 参数3:目标文件输出流 templateEngine.process(templateName, context, writer); logger.info("http path: {}", folder.getAbsolutePath()); // 返回生成文件路径 return folder.getAbsolutePath(); } catch (IOException e) { logger.error("createHtml error {}", ExceptionUtil.getStackTrace(e)); } finally { // flush输出流并关闭 if (writer != null) { writer.flush(); writer.close(); } } return ""; } /** * 根据html生成PDF * * @param html html内容 * @param file 输出pdf文件的路径 * @throws DocumentException * @throws IOException */ @Override public void htmlToPdf(String html, File file) { /** * 切记 css 要定义在head 里,否则解析失败 * css 要定义字体 * 例如宋体style="font-family:SimSun"用simsun.ttc */ if (!file.exists()) { try { if (file.getParentFile() != null && !file.getParentFile().exists()) { file.getParentFile().mkdirs(); } file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } logger.info("开始根据html生成pdf,html={}", html); OutputStream out = null; try { out = new FileOutputStream(file); ITextRenderer renderer = new ITextRenderer(); // 携带图片,将图片标签转换为itext自己的图片对象 renderer.getSharedContext().setReplacedElementFactory(new PdfBase64ImgReplacedElementFactory()); renderer.getSharedContext().getTextRenderer().setSmoothingThreshold(0); // 解决中文支持问题 ITextFontResolver fontResolver = renderer.getFontResolver(); // 设置语言包文件 //设置字体,否则不支持中文,在html中使用字体,html{ font-family: SimSun;} fontResolver.addFont("config/templates/model/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // 如果是本地图片使用 file:,这里指定图片的父级目录。html上写相对路径, // renderer.getSharedContext().setBaseURL("file:/E:/img/") // 处理图片 //renderer.getSharedContext().setBaseURL(IMG_SAVE_URL); renderer.layout(); renderer.createPDF(out); out.flush(); logger.info("pdf生成成功"); } catch (DocumentException e) { logger.error("pdf生成失败,cause--->" + e.getMessage()); } catch (IOException e) { logger.error("pdf生成失败,cause--->" + e.getMessage()); } finally { try { if (null != out) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Override public void htmlToPdf(String htmlPath, String outPath) { /** * 切记 css 要定义在head 里,否则解析失败 * css 要定义字体 * 例如宋体style="font-family:SimSun"用simsun.ttc */ if (StringUtils.isBlank(outPath)) { logger.info("pdf 输出路径为null"); return; } String bufferHtml = readHtmlFile(htmlPath); File file = new File(outPath); if (!file.exists()) { try { if (file.getParentFile() != null && !file.getParentFile().exists()) { file.getParentFile().mkdirs(); } file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } logger.info("开始根据html生成pdf,html={}", htmlPath); OutputStream out = null; try { out = new FileOutputStream(file); ITextRenderer renderer = new ITextRenderer(); // 携带图片,将图片标签转换为itext自己的图片对象 renderer.getSharedContext().setReplacedElementFactory(new PdfBase64ImgReplacedElementFactory()); renderer.getSharedContext().getTextRenderer().setSmoothingThreshold(0); // 解决中文支持问题 ITextFontResolver fontResolver = renderer.getFontResolver(); // 设置语言包文件 //设置字体,否则不支持中文,在html中使用字体,html{ font-family: SimSun;} fontResolver.addFont("config/templates/model/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); renderer.setDocumentFromString(bufferHtml); // 如果是本地图片使用 file:,这里指定图片的父级目录。html上写相对路径, // renderer.getSharedContext().setBaseURL("file:/E:/img/") // 处理图片 //renderer.getSharedContext().setBaseURL(IMG_SAVE_URL); renderer.layout(); renderer.createPDF(out); out.flush(); logger.info("pdf生成成功"); } catch (DocumentException e) { logger.error("pdf生成失败,cause--->" + e.getMessage()); } catch (IOException e) { logger.error("pdf生成失败,cause--->" + e.getMessage()); } finally { try { if (null != out) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 删除id.html * * @param id */ @Override public void deleteHtml(String id) { } public String readHtmlFile(String htmlPath) { File file = new File(htmlPath); if (!file.exists()) { logger.error("html file is not exists, file path: {}", htmlPath); // 文件不存在直接返回 return ""; } BufferedReader reader = null; StringBuffer sbf = new StringBuffer(); try { reader = new BufferedReader(new FileReader(file)); String tempStr; while ((tempStr = reader.readLine()) != null) { sbf.append(tempStr); } reader.close(); return sbf.toString(); } catch (IOException e) { logger.error("error:", e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { logger.error("error:", e); } } } return sbf.toString(); } } 3.1.3 ImgBase64Util import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang.StringUtils; public class ImgBase64Util { public static void main(String[] args) throws Exception { //本地图片地址 String url = "C:/Users/Administrator/Desktop/628947887489084892.jpg"; //在线图片地址 String string = "http://bpic.588ku.com//element_origin_min_pic/17/03/03/7bf4480888f35addcf2ce942701c728a.jpg"; /* String str = Base64Utils.ImageToBase64ByLocal(url); String ste = Base64Utils.ImageToBase64ByOnline(string); System.out.println(str); Base64Utils.Base64ToImage(str,"C:/Users/Administrator/Desktop/test1.jpg"); Base64Utils.Base64ToImage(ste, "C:/Users/Administrator/Desktop/test2.jpg");*/ } /** * 本地图片转换成base64字符串 * * @param imgFile 图片本地路径 * @return */ public static String ImageToBase64ByLocal(String imgFile) {// 将图片文件转化为字节数组字符串,并对其进行Base64编码处理 InputStream in = null; byte[] data = null; // 读取图片字节数组 try { in = new FileInputStream(imgFile); data = new byte[in.available()]; in.read(data); } catch (IOException e) { e.printStackTrace(); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } return DatatypeConverter.printBase64Binary(data); } /** * 在线图片转换成base64字符串 * * @param imgURL 图片线上路径 * @return */ public static String ImageToBase64ByOnline(String imgURL) { ByteArrayOutputStream data = new ByteArrayOutputStream(); InputStream is = null; try { // 创建URL URL url = new URL(imgURL); byte[] by = new byte[1024]; // 创建链接 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); is = conn.getInputStream(); // 将内容读取内存中 int len = -1; while ((len = is.read(by)) != -1) { data.write(by, 0, len); } // 关闭流 } catch (IOException e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); } } catch (Exception e) { e.printStackTrace(); } } return DatatypeConverter.printBase64Binary(data.toByteArray()); // // 对字节数组Base64编码 // BASE64Encoder encoder = new BASE64Encoder(); // return encoder.encode(data.toByteArray()); } /** * base64字符串转换成图片 * * @param imgStr base64字符串 * @param imgFilePath 图片存放路径 * @return */ public static boolean Base64ToImage(String imgStr, String imgFilePath) { // 对字节数组字符串进行Base64解码并生成图片 if (StringUtils.isEmpty(imgStr)) // 图像数据为空 return false; OutputStream out = null; try { byte[] b = DatatypeConverter.parseBase64Binary(imgStr); for (int i = 0; i < b.length; ++i) { if (b[i] < 0) {// 调整异常数据 b[i] += 256; } } out = new FileOutputStream(imgFilePath); out.write(b); out.flush(); return true; } catch (Exception e) { return false; } finally { try { if (out != null) { out.close(); } } catch (Exception e) { e.printStackTrace(); } } } } 3.1.4 PdfBase64ImgReplacedElementFactory html转pdf图片处理自定义工厂 public class PdfBase64ImgReplacedElementFactory implements ReplacedElementFactory { /** * * 实现createReplacedElement 替换html中的Img标签 * * * * @param c 上下文 * * @param box 盒子 * * @param uac 回调 * * @param cssWidth css宽 * * @param cssHeight css高 * * @return ReplacedElement */ @Override public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) { // 遍历所有标签 Element e = box.getElement(); if (e == null) { return null; } String nodeName = e.getNodeName(); // 找到所有img标签 if (nodeName.equals("img")) { String attribute = e.getAttribute("src"); FSImage fsImage; try { // 生成itext图像 fsImage = buildImage(attribute, uac); } catch (BadElementException e1) { fsImage = null; } catch (IOException e1) { fsImage = null; } if (fsImage != null) { // 对图像进行缩放 if (cssWidth != -1 || cssHeight != -1) { fsImage.scale(cssWidth, cssHeight); } return new ITextImageElement(fsImage); } } return null; } /** * 编解码base64并生成itext图像 */ protected FSImage buildImage(String srcAttr, UserAgentCallback uac) throws IOException, BadElementException { FSImage fiImg = null; //图片的src要为src="data:image/jpg;base64,{图片的base64code}"这种base64格式 if (srcAttr.toLowerCase().startsWith("data:image/")) { String base64Code = srcAttr.substring(srcAttr.indexOf("base64,") + "base64,".length(), srcAttr.length()); // 解码 byte[] decodedBytes = Base64.decode(base64Code); fiImg = new ITextFSImage(Image.getInstance(decodedBytes)); } else { fiImg = uac.getImageResource(srcAttr).getImage(); } return fiImg; } @Override public void reset() { } @Override public void remove(Element arg0) { } @Override public void setFormSubmissionListener(FormSubmissionListener arg0) { } } 3.1.7 EcharTemplate.html 模板 <!DOCTYPE html> <html lang="en" xmlns:th="http://·"); // 需要将图片转base64 map.put("imageUrl", "data:image/png;base64," + ImgBase64Util.ImageToBase64ByLocal(imagePath)); map.put("pdf_path", imagePath.substring(0, imagePath.lastIndexOf(".")) + ".pdf"); return map; } } 结果 5 补充

在linux 或是 ubuntu 中文乱码问题解决

在centos中执行:yum install bitmap-fonts bitmap-fonts-cjk 在ubuntu中执行:sudo apt-get install xfonts-wqy


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #springboot #获取 #tyhmeleaf #生成html #文章目录SpringBoot #echars