springboot+itextpdf+thymeleaf+ognl根据静态模版文件实现动态生成pdf文件并导出
- 人工智能
- 2025-07-22 16:24:01

第一步:导入maven依赖 <!-- 导出为PDF依赖包 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> </dependency> <dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.13.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>ognl</groupId> <artifactId>ognl</artifactId> <version>3.1.12</version> </dependency> 第二步:制作thymeleaf静态模版文件并放置在resources目录下,可以自定义模版文件路径
如图所示
thymeleaf静态文件示例(附,在线实时制作预览html文件网址:https://www.jyshare.com/front-end/61/): <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8"/> <title>xxxx模版</title> <style> #mainTable { border-collapse: collapse; border-style: solid; border-width: 0.5pt; width: 100%; } #mainTable td{ text-align: center; padding: 8px; border-style: solid; border-width: 0.5pt; } </style> </head> <body> <table id="header" boder="0" cellpadding="0" cellspacing="0" width="100%" style="margin-bottom:10px;margin-top:10px;"> <tbody> <tr> <td id="img" rowspan="3"><img th:src="${xxxx}" style="border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;-ms-border-radius: 50%;-o-border-radius: 50%;width: 60%;height: 60%;"/></td> <td id="mainTitle" colspan="5" th:text="' '+${xxxx}" style="font-size: 30px;font-style: '+';font-weight: bold;padding-left:30%;padding-bottom:10px;"></td> </tr> <tr> <td></td> <td id="xxxx" colspan="5" th:text="${xxxx}+哈哈" style="font-size: 30px;font-style: '+';font-weight: bold;padding-left:45%;padding-top:10px;"></td> </tr> <tr> <td></td> <td id="xxxx" colspan="5" style="text-align:right;padding-right:0px;">xxxx: <span th:text="${xxxx}"></span></td> </tr> </tbody> </table> <table id="mainTable"> <tbody> <tr> <td>xxxx</td> <td th:text="${xxxx}"></td> <td>xxxx</td> <td th:text="${xxxx}"></td> <td>xxxx</td> <td th:text="${xxxx} + ${xxxx}"></td> </tr> <tr> <td>xxxx</td> <td colspan="2" th:text="${xxxx}"></td> <td>xxxx</td> <td colspan="2" th:text="${xxxx}"></td> </tr> <tr> <td>xxxx</td> <td colspan="2" th:text="${xxxx}"></td> <td>xxxx</td> <td colspan="2" th:text="${xxxx}"></td> </tr> <tr> <td>xxxx</td> <td colspan="2" th:text="${xxxx} + '/' + ${xxxx}"></td> <td>xxxx</td> <td colspan="2" th:text="${xxxx}"></td> </tr> <tr> <td>xxxx</td> <td colspan="5" style="text-align:left;" th:text="${xxxx}"></td> </tr> <tr> <td>xxxx</td> <td colspan="2" th:text="${xxxx}"></td> <td>xxxx</td> <td colspan="2" th:text="${xxxx}"></td> </tr> <tr> <td height="250">xxxx</td> <td colspan="5" style="text-align:left;" th:text="${xxxxx}"></td> </tr> <tr> <td height="250">xxxx</td> <td colspan="5" style="text-align:left;" th:text="${xxxx}"></td> </tr> <tr> <td>xxxx</td> <td colspan="5" style="text-align:left;"><img th:src="${xxxx}" style="width: 50%;height: 30%;"/></td> </tr> </tbody> </table> <div style="margin-top:40px;"> <p>注:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx </p> </div> </body> </html> 第三步:实现编写生成pdf与下载工具,注意区分项目格式是war包还是jar包,目前我这边是两套实现方案。jar包的方案是否同样适用于war包,我这边没有尝试,有兴趣的可以自己尝试,然后在评论区分享一下。
1)在jar包环境下加载静态模版文件时的代码示例: 静态模版无需加载包含base64格式的图片内容时,可用以下代码处理: import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Font; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.tool.xml.XMLWorkerFontProvider; import com.itextpdf.tool.xml.XMLWorkerHelper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.templatemode.TemplateMode; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; /** * @author leon * @date 2023/8/15 * @description pdf 模版下载工具 * */ @Component @Slf4j public class PdfTemplateDownload { @Autowired private ApplicationContext applicationContext; /** * 下载pdf模版 * @param response * @param paramMap * @param templateFileName * @param outputFileName */ public void downloadPdfTemplate(HttpServletResponse response, Map<String, Object> paramMap, String templateFileName, String outputFileName) { try { // 创建基于类路径资源的模板解析器 SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); resolver.setApplicationContext(applicationContext); // 你需要注入Spring的应用上下文 resolver.setTemplateMode(TemplateMode.HTML); resolver.setCharacterEncoding(StandardCharsets.UTF_8.name()); resolver.setCacheable(true); // 在开发阶段设置为false,生产环境可改为true resolver.setPrefix("classpath:/file/"); resolver.setSuffix(".html"); // 创建模版引擎 TemplateEngine engine = new TemplateEngine(); engine.setTemplateResolver(resolver); // 填充变量参数 Context context = new Context(); paramMap.forEach((k,v) -> context.setVariable(k, v)); // 替换变量值 String output = engine.process(templateFileName, context); // 设置导出文件名 String exportName = "attachment;filename="+outputFileName; response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1")); response.setContentType("application/mspdf"); response.setCharacterEncoding("utf-8"); // 新建Document对象 Document document = new Document(PageSize.A4); // 新建PdfWriter对象 PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream()); // 打开文档 document.open(); // 读取html文件内容 InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(StandardCharsets.UTF_8)); // 使用XMLWorkerHelper将html内容转为pdf XMLWorkerHelper xmlWorkerHelper = XMLWorkerHelper.getInstance(); xmlWorkerHelper.parseXHtml(pdfWriter, document, htmlInputStream, Charset.forName("UTF-8"), new AsianFontProvider()); // 关闭文档 document.close(); // 关闭输入流 htmlInputStream.close(); // 关闭文件输出流 IOUtils.closeQuietly(response.getOutputStream()); } catch (IOException e) { log.error("", e); } catch (DocumentException e) { log.error("", e); } } /** * 用于中文显示的Provider */ class AsianFontProvider extends XMLWorkerFontProvider { @Override public Font getFont(final String fontname, String encoding, float size, final int style) { try { BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); return new Font(bfChinese, size, style); } catch (Exception e) { } return super.getFont(fontname, encoding, size, style); } } } 若静态模版中有base64格式的图片信息,可换用下边的方法实现 import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Font; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.tool.xml.Pipeline; import com.itextpdf.tool.xml.XMLWorker; import com.itextpdf.tool.xml.XMLWorkerFontProvider; import com.itextpdf.tool.xml.XMLWorkerHelper; import com.itextpdf.tool.xml.css.CssFilesImpl; import com.itextpdf.tool.xml.css.StyleAttrCSSResolver; import com.itextpdf.tool.xml.html.CssAppliersImpl; import com.itextpdf.tool.xml.html.HTML; import com.itextpdf.tool.xml.html.TagProcessorFactory; import com.itextpdf.tool.xml.html.Tags; import com.itextpdf.tool.xml.parser.XMLParser; import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline; import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline; import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline; import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; import org.thymeleaf.templatemode.TemplateMode; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; /** * @author leon * @date 2023/8/15 * @description pdf 模版下载工具 * */ @Component @Slf4j public class PdfTemplateDownload { @Autowired private ApplicationContext applicationContext; /** * 下载pdf模版,用于模版中需要展示base64格式的图片信息 * @param response * @param paramMap * @param templateFileName * @param outputFileName */ public void downloadPdfTemplateForBase64Img(HttpServletResponse response, Map<String, Object> paramMap, String templateFileName, String outputFileName) { try { // 创建基于类路径资源的模板解析器 SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); resolver.setApplicationContext(applicationContext); // 你需要注入Spring的应用上下文 resolver.setTemplateMode(TemplateMode.HTML); resolver.setCharacterEncoding(StandardCharsets.UTF_8.name()); resolver.setCacheable(true); // 在开发阶段设置为false,生产环境可改为true resolver.setPrefix("classpath:/file/"); resolver.setSuffix(".html"); // 创建模版引擎 TemplateEngine engine = new TemplateEngine(); engine.setTemplateResolver(resolver); // 填充变量参数 Context context = new Context(); paramMap.forEach((k,v) -> context.setVariable(k, v)); // 替换变量值 String output = engine.process(templateFileName, context); // 设置导出文件名 String exportName = "attachment;filename="+outputFileName; response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1")); response.setContentType("application/pdf"); response.setCharacterEncoding("utf-8"); // 新建Document对象 Document document = new Document(PageSize.A4); // 新建PdfWriter对象 PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream()); // 打开文档 document.open(); // 自定义处理base64图片 final TagProcessorFactory htmlTagProcessorFactory = Tags.getHtmlTagProcessorFactory(); htmlTagProcessorFactory.removeProcessor(HTML.Tag.IMG); htmlTagProcessorFactory.addProcessor(new ImageTagProcessor(),HTML.Tag.IMG); final Charset charset = StandardCharsets.UTF_8; final CssFilesImpl cssFiles = new CssFilesImpl(); cssFiles.add(XMLWorkerHelper.getInstance().getDefaultCSS()); final StyleAttrCSSResolver cssResolver = new StyleAttrCSSResolver(cssFiles); final HtmlPipelineContext hpc = new HtmlPipelineContext(new CssAppliersImpl(new AsianFontProvider())); hpc.setAcceptUnknown(true).autoBookmark(true).setTagFactory(htmlTagProcessorFactory); final HtmlPipeline htmlPipeline = new HtmlPipeline(hpc, new PdfWriterPipeline(document, pdfWriter)); final Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline); final XMLWorker worker = new XMLWorker(pipeline, true); final XMLParser p = new XMLParser(true, worker, charset); // 读取html文件内容 InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(charset)); p.parse(htmlInputStream, charset); // 关闭文档 document.close(); // 关闭输入流 htmlInputStream.close(); // 关闭文件输出流 IOUtils.closeQuietly(response.getOutputStream()); } catch (IOException e) { log.error("", e); } catch (DocumentException e) { log.error("", e); } } /** * 用于中文显示的Provider */ class AsianFontProvider extends XMLWorkerFontProvider { @Override public Font getFont(final String fontname, String encoding, float size, final int style) { try { BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); return new Font(bfChinese, size, style); } catch (Exception e) { } return super.getFont(fontname, encoding, size, style); } } } 2)在war包环境下加载静态模版文件时的代码示例: 包含了静态模版中需要加载与不需要加载base64格式的图片内容时的两个方法实现示例: import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Font; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.tool.xml.Pipeline; import com.itextpdf.tool.xml.XMLWorker; import com.itextpdf.tool.xml.XMLWorkerFontProvider; import com.itextpdf.tool.xml.XMLWorkerHelper; import com.itextpdf.tool.xml.css.CssFilesImpl; import com.itextpdf.tool.xml.css.StyleAttrCSSResolver; import com.itextpdf.tool.xml.html.CssAppliersImpl; import com.itextpdf.tool.xml.html.HTML; import com.itextpdf.tool.xml.html.TagProcessorFactory; import com.itextpdf.tool.xml.html.Tags; import com.itextpdf.tool.xml.parser.XMLParser; import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline; import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline; import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline; import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext; import com.wondersgroup.healthcloud.exception.CommonException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.FileTemplateResolver; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; /** * @author leon * @date 2023/8/15 * @description pdf 模版下载工具 * */ @Component @Slf4j public class PdfTemplateDownload { /** * 下载pdf模版 * @param response * @param paramMap * @param templateFileName * @param outputFileName */ public void downloadPdfTemplate(HttpServletResponse response, Map<String, Object> paramMap, String templateFileName, String outputFileName) { try { URL url = PdfTemplateDownload.class.getClassLoader().getResource("file/"+templateFileName); if (ObjectUtils.isEmpty(url)) { throw new CommonException("下载模版文件缺失"); } File htmlFile = new File(url.getPath()); // 创建html文件解析器 FileTemplateResolver resolver = new FileTemplateResolver(); resolver.setTemplateMode(TemplateMode.HTML); resolver.setSuffix(".html"); // 创建模版引擎 TemplateEngine engine = new TemplateEngine(); engine.setTemplateResolver(resolver); // 填充变量参数 Context context = new Context(); paramMap.forEach((k,v) -> context.setVariable(k, v)); // 替换变量值 String output = engine.process(htmlFile.getAbsolutePath(), context); // 设置导出文件名 String exportName = "attachment;filename="+outputFileName; response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1")); response.setContentType("application/pdf"); response.setCharacterEncoding("utf-8"); // 新建Document对象 Document document = new Document(PageSize.A4); // 新建PdfWriter对象 PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream()); // 打开文档 document.open(); // 读取html文件内容 InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(StandardCharsets.UTF_8)); // 使用XMLWorkerHelper将html内容转为pdf XMLWorkerHelper xmlWorkerHelper = XMLWorkerHelper.getInstance(); xmlWorkerHelper.parseXHtml(pdfWriter, document, htmlInputStream, Charset.forName("UTF-8"), new AsianFontProvider()); // 关闭文档 document.close(); // 关闭输入流 htmlInputStream.close(); // 关闭文件输出流 IOUtils.closeQuietly(response.getOutputStream()); } catch (IOException e) { log.error("", e); } catch (DocumentException e) { log.error("", e); } } /** * 下载pdf模版,用于模版中需要展示base64格式的图片信息 * @param response * @param paramMap * @param templateFileName * @param outputFileName */ public void downloadPdfTemplateForBase64Img(HttpServletResponse response, Map<String, Object> paramMap, String templateFileName, String outputFileName) { try { URL url = PdfTemplateDownload.class.getClassLoader().getResource("file/"+templateFileName); if (ObjectUtils.isEmpty(url)) { throw new CommonException("下载模版文件缺失"); } File htmlFile = new File(url.getPath()); // 创建html文件解析器 FileTemplateResolver resolver = new FileTemplateResolver(); resolver.setTemplateMode(TemplateMode.HTML); resolver.setSuffix(".html"); // 创建模版引擎 TemplateEngine engine = new TemplateEngine(); engine.setTemplateResolver(resolver); // 填充变量参数 Context context = new Context(); paramMap.forEach((k,v) -> context.setVariable(k, v)); // 替换变量值 String output = engine.process(htmlFile.getAbsolutePath(), context); // 设置导出文件名 String exportName = "attachment;filename="+outputFileName; response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1")); response.setContentType("application/pdf"); response.setCharacterEncoding("utf-8"); // 新建Document对象 Document document = new Document(PageSize.A4); // 新建PdfWriter对象 PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream()); // 打开文档 document.open(); // 自定义处理base64图片 final TagProcessorFactory htmlTagProcessorFactory = Tags.getHtmlTagProcessorFactory(); htmlTagProcessorFactory.removeProcessor(HTML.Tag.IMG); htmlTagProcessorFactory.addProcessor(new ImageTagProcessor(),HTML.Tag.IMG); final Charset charset = StandardCharsets.UTF_8; final CssFilesImpl cssFiles = new CssFilesImpl(); cssFiles.add(XMLWorkerHelper.getInstance().getDefaultCSS()); final StyleAttrCSSResolver cssResolver = new StyleAttrCSSResolver(cssFiles); final HtmlPipelineContext hpc = new HtmlPipelineContext(new CssAppliersImpl(new AsianFontProvider())); hpc.setAcceptUnknown(true).autoBookmark(true).setTagFactory(htmlTagProcessorFactory); final HtmlPipeline htmlPipeline = new HtmlPipeline(hpc, new PdfWriterPipeline(document, pdfWriter)); final Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline); final XMLWorker worker = new XMLWorker(pipeline, true); final XMLParser p = new XMLParser(true, worker, charset); // 读取html文件内容 InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(charset)); p.parse(htmlInputStream, charset); // 关闭文档 document.close(); // 关闭输入流 htmlInputStream.close(); // 关闭文件输出流 IOUtils.closeQuietly(response.getOutputStream()); } catch (IOException e) { log.error("", e); } catch (DocumentException e) { log.error("", e); } } /** * 用于中文显示的Provider */ class AsianFontProvider extends XMLWorkerFontProvider { @Override public Font getFont(final String fontname, String encoding, float size, final int style) { try { BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); return new Font(bfChinese, size, style); } catch (Exception e) { } return super.getFont(fontname, encoding, size, style); } } }springboot+itextpdf+thymeleaf+ognl根据静态模版文件实现动态生成pdf文件并导出由讯客互联人工智能栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“springboot+itextpdf+thymeleaf+ognl根据静态模版文件实现动态生成pdf文件并导出”