700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Java使用Thylemeaf + iText实现html(带图片)转pdf文件

Java使用Thylemeaf + iText实现html(带图片)转pdf文件

时间:2018-12-15 04:21:58

相关推荐

Java使用Thylemeaf + iText实现html(带图片)转pdf文件

基于SpringBoot使用Thymeleaf+iText实现html(带图片)转pdf文件

1.导入依赖

<!-- Thymeleaf 模板引擎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf</artifactId><version>9.1.6</version></dependency><!-- /artifact/ognl/ognl 这个依赖不引入会抛异常--><dependency><groupId>ognl</groupId><artifactId>ognl</artifactId><version>3.1.12</version></dependency><!--io常用工具类 --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.5</version></dependency><!-- /artifact/org.jsoup/jsoup --><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.7.1</version></dependency>

2.创建thylemeaf模板

根据模板生成文件,可以在模板里指定格式,在 resources/templtes目录下创建一个模板

htmlTemplate.html文件就是我的模板,font/SIMSUN.TTC文件解决转pdf是解决中文的字体,后面需要引入。

编写模板htmlTemplate.html

<!DOCTYPE html><html lang="en" xmlns:th=""><head><style type="text/css">body {font-family: SimSun;}.content_wrap {padding: 20px 45px;background: white;-webkit-print-color-adjust: exact;border-radius: 4px;}._header {position: relative;font-size: 18px;line-height: 1;color: black;}._header::after {position: absolute;left: -15px;top: -10%;transform: translateY(-50%);content: "";display: block;width: 5px;height: 20px;background-color: #00bba6;-webkit-print-color-adjust: exact;}.infos {margin: 10px 0 30px 0;padding: 20px 0px;}.infos span {width: 33%;margin-top: 20px;margin-right: 10px;}._info-label {font-size: 14px;font-weight: 400;color: #8E97A5;line-height: 14px;margin-right: 10px;}._info-value {font-size: 14px;font-weight: 400;color: #2A3245;line-height: 14px;}._table {margin-top: 20px;border: 1px solid #E1E6EC;border-collapse: collapse;}._table tr {border-bottom: 1px solid #E1E6EC;height: 48px;}._table ._tr-val {height: 60px;}td {border-top: 0;border-right: 1px #E1E6EC solid;border-bottom: 1px #E1E6EC solid;border-left: 0;text-align: right;}table {border-top: 1px #E1E6EC solid;border-right: 0;border-bottom: 0;border-left: 1px #E1E6EC solid;}._table td {text-align: right;padding-right: 10px;font-weight: bold;font-size: 14px;color: #2A3245;border-right: 1px solid #E1E6EC;}._table .t-tithle {background: #F3F6F9;-webkit-print-color-adjust: exact;}._foot p {margin-top: 20px;}._foot ._label {font-size: 14px;font-weight: 400;color: #5F677A;line-height: 14px;margin-right: 45px;}._foot ._value {font-size: 20px;font-weight: bold;color: #2A3245;line-height: 20px;}</style></head><body><!-- 使用 utext 可以识别html标签--><div class="detail-content content_wrap pl30" th:utext="${content}"></div></body></html>

3.有图片,需要把图片转成 iText 的图片对象,需要转成Base64编码,用到如下类

package com.cqbay.maserb.factory;import com.lowagie.text.BadElementException;import com.lowagie.text.Image;import com.lowagie.text.pdf.codec.Base64;import org.w3c.dom.Element;import org.xhtmlrenderer.extend.FSImage;import org.xhtmlrenderer.extend.ReplacedElement;import org.xhtmlrenderer.extend.ReplacedElementFactory;import org.xhtmlrenderer.extend.UserAgentCallback;import org.xhtmlrenderer.layout.LayoutContext;import org.xhtmlrenderer.pdf.ITextFSImage;import org.xhtmlrenderer.pdf.ITextImageElement;import org.xhtmlrenderer.render.BlockBox;import org.xhtmlrenderer.simple.extend.FormSubmissionListener;import java.io.IOException;/*** @ClassName: Base64ImgReplacedElementFactory* @Description: TODO* @Author: Jane* @Date: /7/8 14:07* @Version: V1.0**/public class Base64ImgReplacedElementFactory implements ReplacedElementFactory {/*** * 实现createReplacedElement 替换html中的Img标签* ** * @param c 上下文* * @param box 盒子* * @param uac 回调* * @param cssWidth css宽* * @param cssHeight css高* * @return ReplacedElement**/@Overridepublic 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="https://img-/202616555048137.jpg"这种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;}@Overridepublic void reset() {}@Overridepublic void remove(Element arg0) {}@Overridepublic void setFormSubmissionListener(FormSubmissionListener arg0) {}}

4.生成PDF的工具类

PdfUtils

package com.cqbay.maserb;import com.lowagie.text.DocumentException;import lombok.extern.slf4j.Slf4j;import org.jsoup.Jsoup;import org.jsoup.nodes.Document;import org.jsoup.nodes.Element;import org.jsoup.select.Elements;import com.cqbay.maserb.factory.Base64ImgReplacedElementFactoryimport org.springframework.core.io.ClassPathResource;import org.springframework.core.io.Resource;import org.springframework.util.ObjectUtils;import org.springframework.util.StringUtils;import org.thymeleaf.TemplateEngine;import org.thymeleaf.context.Context;import org.xhtmlrenderer.pdf.ITextFontResolver;import org.xhtmlrenderer.pdf.ITextRenderer;import java.io.*;import .URL;import .URLConnection;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/*** @ClassName: PdfUtils* @Description: pdf工具类* @Author: Jane* @Date: /7/8 14:27**/@Slf4jpublic class PdfUtils {/*** 字体路径*/public static String FONT_PATH = "font/SIMSUN.TTC";/*** html模板路径*/public static final String HTML_TEMPLATE_PATH = "templates/pdf/htmlTemplate.html";/*** 生成的pdf保存目录,这里是固定了目录,文件名需自己拼装*/public static final String PDF_BASE_PATH = "src/main/resources/pdf/";/*** PDF扩展名*/public static final String PDF_EXTENSION = ".pdf";/*** 图片基础URL*/public static final String IMG_SAVE_URL = "http://117.78.37.58:8989/files/";/*** 图片保存路径*/public static final String IMG_SAVE_PATH = "src/main/resources/img/";public static TemplateEngine templateEngine = new TemplateEngine();/*** 读取文件** @param file* @return 读取文件的内容*/public static String getFileString(File file) {log.info("开始读取文件");BufferedReader reader = null;StringBuffer sb = new StringBuffer();try {reader = new BufferedReader(new FileReader(file));String tempStr;while ((tempStr = reader.readLine()) != null) {sb.append(tempStr);}reader.close();log.info("读取文件完成,文件信息:file={}", sb.toString());return sb.toString();} catch (IOException e) {log.error("文件读取失败,cause--->" + e.getMessage());} finally {if (reader != null) {try {reader.close();} catch (IOException e1) {e1.printStackTrace();}}}return sb.toString();}/*** 根据模板生成html文件** @param htmlTemplatePath html模板* @param content填充内容* @return 根据模板生成的html* @throws IOException*/public static String getHtml(String htmlTemplatePath, String content) {if (StringUtils.isEmpty(htmlTemplatePath)) {htmlTemplatePath = HTML_TEMPLATE_PATH;}if (StringUtils.isEmpty(content)) {log.debug("生成html失败:内容content为未空");return null;}try {log.info("开始是生成html文件");Resource resource = new ClassPathResource(htmlTemplatePath);File sourceFile = resource.getFile();Context context = new Context();// 将内容写入模板Map<String, Object> params = new HashMap<>();params.put("content", content);context.setVariables(params);return templateEngine.process(getFileString(sourceFile), context);} catch (IOException e) {log.error("html文件生成失败:cause--->" + e.getMessage());return null;}}/*** 根据html生成PDF** @param html html内容* @param file 输出pdf文件的路径* @throws DocumentException* @throws IOException*/public static 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();}}log.info("开始根据html生成pdf,html={}", html);OutputStream out = null;try {out = new FileOutputStream(file);ITextRenderer renderer = new ITextRenderer();// 携带图片,将图片标签转换为itext自己的图片对象renderer.getSharedContext().setReplacedElementFactory(new Base64ImgReplacedElementFactory());renderer.getSharedContext().getTextRenderer().setSmoothingThreshold(0);// 解决中文支持问题ITextFontResolver fontResolver = renderer.getFontResolver();// 字体名称要大写,否则可能找不到fontResolver.addFont(FONT_PATH, "Identity-H", false);renderer.setDocumentFromString(html);// 如果是本地图片使用 file:,这里指定图片的父级目录。html上写相对路径,// renderer.getSharedContext().setBaseURL("file:/E:/img/")// 处理图片renderer.getSharedContext().setBaseURL(IMG_SAVE_URL);renderer.layout();renderer.createPDF(out);out.flush();log.info("pdf生成成功");} catch (DocumentException e) {log.error("pdf生成失败,cause--->" + e.getMessage());} catch (IOException e) {log.error("pdf生成失败,cause--->" + e.getMessage());} finally {try {if (null != out) {out.close();}} catch (IOException e) {e.printStackTrace();}}}/*** 替换http图片url为其相对url* 例如:http://117.78.37.58:8989/files/2027\png\dcb09254b0d049d28550a9f31d8e88af.png* 替换成:2027/png/dcb09254b0d049d28550a9f31d8e88af.png* 注意:一定要把 url中的所有 "\" 替换成 "/" 否者图片可能不会显示,原因不明** @param html* @return html字符串*/public static String replaceImgTagSrc(String html) {log.info("开始替换图片");if (StringUtils.isEmpty(html)) {log.debug("图片替换入参html为空");return null;}// 解析htmlDocument document = Jsoup.parse(html);Elements imgList = document.getElementsByTag("img");if (ObjectUtils.isEmpty(imgList) || imgList.size() == 0) {log.debug("html中没有图片需要替换");return html;}List<String> srcList = new ArrayList();for (Element img : imgList) {// 获取src的值String src = img.attr("src");srcList.add(src);}log.info("html中img标签src值列表srcList={}", srcList);// 遍历下载图片// List<String> imgPathList = downloadImg(srcList);// 遍历获取图片相对路径List<String> subImgUrlList = new ArrayList();for (String imgUrl : srcList) {// 我这里是用的http图片,所有图片都放在 files 下的,所以从 files/ 后面开始截取// 获取图片相对路径,并把路径中的 "\" 替换成 "/"String subImgUrl = imgUrl.substring(imgUrl.indexOf("files") + "files".length() + 1).replaceAll("\\\\", "/");subImgUrlList.add(subImgUrl);}log.info("图片子路径列表subImgUrlList={}", subImgUrlList);// 替换for (int i = 0; i < imgList.size(); i++) {imgList.get(i).attr("src", subImgUrlList.get(i));}log.info("图片替换完成后的html={}", document.toString());return document.toString();}/*** 批量下载图片** @param imgUrlList 图片链接* @return List<String> 本地存储路径列表*/public static List<String> downloadImg(List<String> imgUrlList) {try {if (ObjectUtils.isEmpty(imgUrlList) || imgUrlList.size() == 0) {log.info("图片路径列表入参不能为空");return null;}List<String> imgPathList = new ArrayList();for (String imgUrl : imgUrlList) {if (!"".equals(imgUrl)) {String replaceImgUrl = "";if (imgUrl.contains("\\")) {replaceImgUrl = imgUrl.replaceAll("\\\\", "/");} else {replaceImgUrl = imgUrl;}String fileName = replaceImgUrl.substring(replaceImgUrl.lastIndexOf("/") + 1);String localImgPath = System.getProperty("user.dir") + "/" + IMG_SAVE_PATH + fileName;// 下载URL url = new URL(imgUrl);URLConnection connection = url.openConnection();InputStream is = connection.getInputStream();byte[] bs = new byte[1024];int len;File file = new File(localImgPath);// 图片不存在,下载图片if (!file.exists()) {try {if (file.getParentFile() != null && !file.getParentFile().exists()) {file.getParentFile().mkdirs();}file.createNewFile();} catch (IOException e) {e.printStackTrace();}FileOutputStream os = new FileOutputStream(file, true);while ((len = is.read(bs)) != -1) {os.write(bs, 0, len);os.flush();}os.close();is.close();imgPathList.add(localImgPath);} else {// 图片存在,直接使用imgPathList.add(localImgPath);}} else {imgPathList.add(imgUrl);}}return imgPathList;} catch (IOException e) {e.printStackTrace();return null;}}}

5.运行测试

package com.cqbay.maserb;import java.io.File;/*** @ClassName: TestMain* @Description: TODO* @Author: Jane* @Date: /7/8 15:22* @Version: V1.0**/public class PdfTest {// html片段 private static final String content = "<p style=\"text-align: justify;\"><img class=\"wscnph\" src=\"http://117.78.37.58:8989/files/2027\\png\\dcb09254b0d049d28550a9f31d8e88af.png\" /></p>\n" +"<p style=\"text-align: left;\">犀牛是国bai家稀有动物之一,也是du国家级保护动物。</p>\n" +"<p style=\"text-align: left;\">&nbsp;</p>\n" +"<p style=\"text-align: left;\">犀牛身体庞大bai,四肢粗du壮,体重一般都在三千斤左右。它的皮又厚又硬,足以挡住任何动物的袭击。犀牛鼻子上张着一只或两只坚硬的角,在动物王国里抵抗力和杀伤力都是数一数二的。任何猛兽连人都难以打倒它,它发起怒来连附近的树木植物都难逃厄运,就连狮子、老虎等大型陆地动物在犀牛发怒时都得逃之夭夭。</p>\n" +"<p style=\"text-align: left;\">&nbsp;</p>\n" +"<p style=\"text-align: left;\">犀牛的皮肤虽然厚糙,可皮肤中的细缝却柔嫩,成为了寄生虫、蚊子等吸血昆虫的青睐。可它有一位如影随形的好朋友——犀牛鸟,它以犀牛皮肤空隙里的吸血虫为食。这样既帮助犀牛除出祸害,又让自己饱餐一顿,可真是一举两得啊!</p>\n" +"<p style=\"text-align: left;\">&nbsp;</p>\n" +"<p style=\"text-align: left;\">犀牛拥有高度近视,它的好朋友犀牛鸟却视力良好。在发现情敌的时候,犀牛鸟就会“叽叽喳喳”向犀牛提醒。这时,犀牛就会迅速逃离现场,让敌人枉费心机。</p>\n" +"<p style=\"text-align: left;\">&nbsp;</p>\n" +"<p style=\"text-align: left;\">虽然犀牛以稀有而收到世人的保护,可仍有一些不法之徒向犀牛伸出魔爪,让我们一起来保护犀牛,保护野生动物吧!!!</p>\n" +"<p style=\"text-align: left;\">&nbsp;</p>";public static void main(String[] args) {// 把html片段中的图片url替换成相对url,采用Jsoup解析String replaceImgTagSrc = PdfUtils.replaceImgTagSrc(content);//把替换后的html片段根据Thymeleaf模板生成htmlString html = PdfUtils.getHtml(PdfUtils.HTML_TEMPLATE_PATH, replaceImgTagSrc);// 把html转成pdf,这里将生成的pdf文件放在E盘下PdfUtils.htmlToPdf(html, new File("E:/pdfTest.pdf"));}}

6.测试结果

参考:</yunfeiyang-88/p/10984740.html

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。