首页IT科技freemarker动态生成word表格(使用Freemarker来生成pdf文件)

freemarker动态生成word表格(使用Freemarker来生成pdf文件)

时间2025-06-20 20:57:53分类IT科技浏览5719
导读:2022-09-02...

2022-09-02

        今天接到一个生成pdf的任务             ,并且web端要能下载;在网上也找了许多的工具如:itext等                   ,感觉挺复杂的没那么好用      ,然后想起了之前使用Freemarker来生成world文档             ,挺好用的                   ,然后调查发现也能生成pdf      ,就是有一点区别如果Freemarker来生成world是使用world文档来当模板       ,而pdf相对于简单                   ,直接使用html文件来制作模板             ,只不过最后要将文件后缀改成ftl的文件             。

这个博主写的挺好的       ,可以直接去看这个博主的文章                   ,我只是当笔记记录一下             ,参考的文章链接

本文链接:Java使用Freemarker通过模板文件导出PDF文件             、横向显示_虚心若愚求知若渴的博客-CSDN博客_freemarker ftl生成pdf前言:​尝试了不少方法通过模板文件导出pdf文件,要么实现起来负责                   ,要么实现效果不理想                   ,经过反复查找资料发现此方法是最理想的                   。一,依赖jar包<!-- freemarker 读取html模板文件 --><dependency> <groupId>org.freemarker</groupId> &https://blog.csdn.net/weixin_39806100/article/details/86616041

代码如下:

maven依赖:

<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.29</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf</artifactId> <version>9.1.18</version> </dependency>

service层:

public void exportPdf(HttpServletResponse response, Integer id, Integer type) throws Exception { ByteArrayOutputStream baos = null; OutputStream out = null; FileOutputStream fileOutputStream = null; try { //获取提货单数据             ,根据提货单id TakeOrder takeOrder = this.getTakeById(id); //翻译提货单状态 String[] stateName = {"待备货","备货中","已备货","已出库","装车中","已装车","已进厂","已出厂"}; takeOrder.setStateName(takeOrder.getState() == null ? "" : stateName[takeOrder.getState() - 1]); //翻译提货单提货状态 String[] orderStateName = {"待提货","已提货","作废"}; takeOrder.setOrderStateName(orderStateName[takeOrder.getOrderState() - 1]); // 模板中的数据                   ,实际运用从数据库中查询 Map<String,Object> data = new HashMap<>(); data.put("takeOrder", takeOrder); data.put("fileName", type == 1 ? "备货联" : "承运联"); //因为我自己的需求有两套模板      ,所以我让模板名称动态化了             ,如果不用直接删除这个type参数                   ,正常填文件名称就可以      ,记得带上后缀 baos = PDFTemplateUtil.createPDF(data, "modezs"+type+".ftl"); // 设置响应消息头       ,告诉浏览器当前响应是一个下载文件 response.setContentType( "application/x-msdownload"); // 告诉浏览器                   ,当前响应数据要求用户干预保存到文件中             ,以及文件名是什么 如果文件名有中文       ,必须URL编码 String fileName = URLEncoder.encode("月度报告.pdf", "UTF-8"); response.setHeader( "Content-Disposition", "attachment;filename=" + fileName); out = response.getOutputStream(); baos.writeTo(out); baos.close(); //下载到本地位置 // fileOutputStream = new FileOutputStream("D:\\zscProject\\zsc.pdf"); //生成pdf完成记录行为记录 this.addActionLog(takeOrder.getTakeOrderNo(),1); } catch (Exception e) { e.printStackTrace(); throw new Exception("导出失败:" + e.getMessage()); } finally{ if(baos != null){ baos.close(); } if(out != null){ out.close(); } if (fileOutputStream != null){ fileOutputStream.close(); } } }

 ps:

1. 在使用工具类时                   ,传文件名称的参数

 2. 在如果不需要web端的方式下载pdf             ,可以使用文件输出流直接下载到本地

工具类:

可以直接拿来使用

public class PDFTemplateUtil { /** * 通过模板导出pdf文件 * @param data 数据 * @param templateFileName 模板文件名 * @throws Exception */ public static ByteArrayOutputStream createPDF(Map<String,Object> data, String templateFileName) throws Exception { // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例 Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); // 指定FreeMarker模板文件的位置 cfg.setClassForTemplateLoading(PDFTemplateUtil.class,"/templates"); ITextRenderer renderer = new ITextRenderer(); OutputStream out = new ByteArrayOutputStream(); try { // 设置 css中 的字体样式(暂时仅支持宋体和黑体) 必须,不然中文不显示 renderer.getFontResolver().addFont("/templates/font/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // 设置模板的编码格式 cfg.setEncoding(Locale.CHINA, "UTF-8"); // 获取模板文件 Template template = cfg.getTemplate(templateFileName, "UTF-8"); StringWriter writer = new StringWriter(); // 将数据输出到html中 template.process(data, writer); writer.flush(); String html = writer.toString(); // 把html代码传入渲染器中 renderer.setDocumentFromString(html); // 设置模板中的图片路径 (这里的images在resources目录下) 模板中img标签src路径需要相对路径加图片名 如<img src="images/xh.jpg"/> // URI images = PDFTemplateUtil.class.getClassLoader().getResource("images").toURI(); // if (images != null) { // String url = images.toString(); // renderer.getSharedContext().setBaseURL(url); // } renderer.layout(); renderer.createPDF(out, false); renderer.finishPDF(); out.flush(); return (ByteArrayOutputStream)out; } finally { if(out != null){ out.close(); } } } }

ps:

我导出pdf里面是含有图片的                   ,但是我的图片是base64的字节码(建议使用这种方式)                   ,不是本地的方式填充数据的,如果只能使用本地的图片             ,将工具类中的这段代码解开                   ,然后自己改进一下

模板文件:

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: SimSun; padding: 30px 20px 0; } section { display: block; /* margin: 20px 10px; */ } .title { text-align: center; margin-bottom: 20px; } .preface p { line-height: 30px; display: inline-block; } .preface p.content { text-indent: 2em; } section>table { border-collapse: collapse; table-layout: fixed; width: 100%; font-size: 13px; /* margin: 20px 0px; */ text-align: center; word-wrap: break-word; } section table td { padding: 5px 0px; } .topTitle section{ width: 30%; font-size: 13px; display: inline-block; margin-top: 20px; } .topTitle{ } .outTitle{ } .outTitle section{ font-size: 13px; display: inline-block; } .detail{ margin-top: 20px; } .outTable{ margin-bottom: 20px; } .box1{ } .box2{ width: 80%; display: inline-block; } .box3{ display: inline-block; width: 18%; /* min-width: 180px; */ } .box3 img{ width: 100%; } .box3 p{ font-size: 12px; } </style> </head> <body> <h3>${(fileName)!}</h3> <div class="box1"> <section class="title"> <h2>XXXXXXXXXXXXXX有限公司</h2> <h2>提货单</h2> </section> <div class="box2"> <!-- 标题 start --> <!-- 标题 end --> <!-- 前言 start --> <div class="topTitle"> <section class="preface"> <p>提货单号:</p> <p>${(takeOrder.takeOrderNo)!}</p> </section> <section class="preface"> <p>提货日期:</p> <p>${(takeOrder.takeDate)!}</p> </section> <section class="preface"> <p>提货状态:</p> <p>${(takeOrder.orderStateName)!}</p> </section> <section class="preface"> <p>状态:</p> <p>${(takeOrder.stateName)!}</p> </section> <#-- <section class="preface">--> <#-- <p>承运商:</p>--> <#-- <p>${(takeOrder.takeOrderNo)!}</p>--> <#-- </section>--> <#-- <section class="preface">--> <#-- <p>车辆:</p>--> <#-- <p>${(takeOrder.takeOrderNo)!}</p>--> <#-- </section>--> <section class="preface"> <p>司机:</p> <p>${(takeOrder.driver)!}</p> </section> <section class="preface"> <p>发运方式:</p> <p>${(takeOrder.shippingMethod)!}</p> </section> </div> </div> <div class="box3"> <img src="${(takeOrder.qrCode)!}"></img> <p>凭此二维码进出厂区</p> </div> </div> <!-- 前言 end --> <!-- 产品列表 start --> <#if takeOrder.outOrderProducts ??> <section class="detail"> <table border="1" cellspacing="0" cellpadding="0"> <tr> <td width="15%">品名编号</td> <td width="12%">品名</td> <td width="12%">规格型号</td> <td width="12%">销售型号</td> <td width="12%">包装规格</td> <td width="12%">批号</td> <td width="12%">数量</td> <td width="12%">单位</td> <td width="12%">仓库编号</td> <td width="12%">仓库名称</td> </tr> <#list takeOrder.outOrderProducts as ad> <tr> <td>${(ad.productCode)!}</td> <td>${(ad.productName)!}</td> <td>${(ad.typeNum)!}</td> <td>${(ad.saleType)!}</td> <td>${(ad.packSize)!}</td> <td>${(ad.batchNumber)!}</td> <td>${(ad.num)!}</td> <td>${(ad.uint)!}</td> <td>${(ad.stockNo)!}</td> <td>${(ad.stockName)!}</td> </tr> </#list> </table> </section> </#if> <!-- 产品列表 end --> <!-- 出库单 start --> <#if takeOrder.outOrders ??> <section class="detail"> <h3>出库单信息:</h3> <#list takeOrder.outOrders as add> <div class="outTitle" > <section class="preface"> <p>出库单号:</p> <p>${(add.outOrderNo)!}</p> </section> <section class="preface"> <p>发货单号:</p> <p>${(add.sendOrderNo)!}</p> </section> <section class="preface"> <p>出库日期:</p> <p>${(add.outDate)!}</p> </section> <section class="preface"> <p>装车号:</p> <p>${(add.loadingNumber)!}</p> </section> <section class="preface"> <p>客户名称:</p> <p>${(add.customerName)!}</p> </section> </div> <!--出库的单产品列表--> <#if add.outOrderProducts ??> <table class="outTable" border="1" cellspacing="0" cellpadding="0"> <tr> <td width="15%">品名编号</td> <td width="12%">品名</td> <td width="12%">规格型号</td> <td width="12%">客户销售型号</td> <td width="12%">包装规格</td> <td width="12%">批号</td> <td width="12%">数量</td> <td width="12%">内部备注</td> <td width="12%">备注</td> </tr> <#list add.outOrderProducts as ad> <tr> <td>${(ad.productCode)!}</td> <td>${(ad.productName)!}</td> <td>${(ad.typeNum)!}</td> <td>${(ad.saleType)!}</td> <td>${(ad.packSize)!}</td> <td>${(ad.batchNumber)!}</td> <td>${(ad.num)!}</td> <td>${(ad.innerRemark)!}</td> <td>${(ad.remark)!}</td> </tr> </#list> </table> </#if> </#list> </section> </#if> <!-- 出库单 end --> </body> </html>

ps:

1.这里面的样式是按照html的样式来的      ,自己设计的要自己调整样式             ,只支持定位和浮动                   ,不支持自适应      ,建议在class里写样式       ,而不是style里写样式

2.像<#if takeOrder.outOrderProducts ??>                   ,<#list takeOrder.outOrderProducts as ad>和${(fileName)!}这个都是ftl文件的语法             ,不懂的可以搜一下

占位符可以看下图

来自本文链接:简单的Freemarker判断对象是否为空方法_OxYGC的博客-CSDN博客_freemarker if判断为空Freemarker判断对象是否为空1. freemarker中显示某对象使用${name}.但如果name为null       ,freemarker就会报错      。如果需要判断对象是否为空:2. 当然也可以通过设置默认值${name!’’}来避免对象为空的错误       。如果name为空                   ,就以默认值(“!             ”后的字符)显示                   。3. 对象user             ,name为user的属性的情况,user                   ,name都有可能为空                   ,那么......https://blog.csdn.net/YangCheney/article/details/1058324443.放置位置如图

字体文件:

在windows10系统中的 C:\Windows\Fonts 这个路径中,进入后搜索宋体(ps:一定要搜索宋体             ,不要按照simsun这个名字去搜索                   ,反正就是注意文件的后缀是.ttc的      ,而不是.ttf的

前端:

1.请求方法js:(ps:注意里面的responseType: arraybuffer这个参数             ,因为后台使用的字节数组流的方式写入的                   ,所以如果直接使用responseType: blob      ,会导致封装的blob对象是有问题的       ,下载出来的pdf文件损坏                   ,本文链接:

使用FreeMarker生成pdf时             ,代码没异常产生       ,但是web端下载下来的文件损坏_A-Superman的博客-CSDN博客使用FreeMarker生成pdf时                   ,代码没异常产生             ,但是web端下载下来的文件损坏https://blog.csdn.net/Jackbillzsc/article/details/126662319

export function exportPdf(parameter) { return request({ url: XXXXXXXXXXXXXXX/export/pdf, method: get, params:parameter, responseType: arraybuffer, }) }

2.封装blob对象,下载pdf的js方法:

exportPdf(type) { this[loading+type] = true exportPdf({ id: this.pageList.id, type: type }).then((res) => { if (!res) { alert(数据为空) return } const content = res const blob = new Blob([content], { type: application/pdf }) // const fileName = titName?titName: let fileName = this.pageList.takeOrderNo if (download in document.createElement(a)) { // 非IE下载 const elink = document.createElement(a) elink.download = fileName elink.style.display = none elink.href = URL.createObjectURL(blob) document.body.appendChild(elink) elink.click() URL.revokeObjectURL(elink.href) // 释放URL 对象 document.body.removeChild(elink) this[loading+type] = false } else { // IE10+下载 navigator.msSaveBlob(blob, fileName) } }) },

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
怎么推广网站宣传(网站如何推广好)