PDF技术方案-wkhtmltopdf

PDF技术方案-wkhtmltopdf_第1张图片
前端实现导出 PDF 产品报告,存在几个问题: 1. 是图片版的 PDF; 2. PDF 太大,会卡; 3. 可能会把文字裁剪分页; 4. 无法满足平台提供 Api 接口服务。核心就是问题3和问题4,于是,考虑后端服务实现导出 PDF 产品报告的方案。Java 实现 HTML 转 PDF 技术选型
PDF技术方案-wkhtmltopdf_第2张图片
推荐使用 wkhtmltopdf, Itext,但 wkhtmltopdf 开源免费,Itext 需要考虑版权参考:https://blog.csdn.net/weixin_43981813/article/details/128135730参考:https://www.cnblogs.com/IT-study/p/13706690.html技术实现方案技术采用模板引擎 + PDF 插件实现。开发好页面模板,Thymeleaf 模板引擎渲染静态 HTML 文件,wkhtmltopdf 将静态的 HTML 生成 PDF 文件。整体方案流程如下:
图片
后台使用 Thymeleaf 模板生成 Html 报告页面PDF 接口根据 ID 查询报告数据调用 wkhtmltopdf 工具 将 Html 转为 PDF 文件关于 wkhtmltopdf参数文档: https://wkhtmltopdf.org/usage/wkhtmltopdf.txtwkhtmltopdf 安装Yum 安装(可能是老版本存在bug,不推荐)yum -y install wkhtmltopdfrpm 包安装下载最新按照包,如wget https://objects.githubusercontent.com/github-production-relea...先安装依赖包yum install -y fontconfig libX11 libXext libXrender libjpeg libpng xorg-x11-fonts-75dpi xorg-x11-fonts-Type1wkhtmltox 安装rpm -ivh wkhtmltox-0.12.6-1.centos8.x86_64.rpm若需要路径执行,可配置cp /usr/local/bin/wkhtmltopdf /usr/bin/wkhtmltopdf内网安装先下载依赖包到指定目录,例如下载 openssl 依赖包到指定目录yum install --downloadonly --downloaddir=/usr/soft/wktooltopdf/ openssl之后,拷贝依赖包到内网环境,执行命令rpm -ivh --force --nodeps .rpmrpm -ivh --force --nodeps .rpm常见问题缺少依赖包手动安装依赖包FAQ-linux 安装 wkhtmltopdf 中文乱码或者空白解决方法参考:https://www.cnblogs.com/jluo/p/17403785.html安装中文字体,或复制已有字体打开windows c:\Windows\fonts\simsun.ttc
拷贝到linux服务器/usr/share/fonts/目录下,再次生成pdf中文显示正常出现错误: wkhtmltopdf:cannot connect to X server参考:https://www.jianshu.com/p/2cfc02961528需再安装xvfbyum install xorg-x11-server-Xvfb在 /usr/bin/ 目录下生成脚本 wkhtmltopdf.sh 并写入命令sudo vim /usr/bin/wkhtmltopdf.sh
命令:
xvfb-run -a --server-args="-screen 0, 1024x768x24" /usr/bin/wkhtmltopdf -q $*更改文件权限并建立连接chmod a+x /usr/bin/wkhtmltopdf.sh
ln -s /usr/bin/wkhtmltopdf.sh /usr/local/bin/wkhtmltopdf中文字体安装若出现中文乱码,则可能是缺少字体阿里巴巴普惠体2.0,免费无版权,好用下载地址: https://done.alibabadesign.com/puhuiti2.0介绍说明: https://fonts.adobe.com/fonts/alibaba-puhuiti设置字体集font-family: alibaba-puhuiti, sans-serif;
font-style: normal;
font-weight: 300;开发代码及配置静态资源目录位于 *-model 工程下的资源文件,包括以下目录templates/ - 模板文件
static/ - 静态资源文件若前端有修改调整,需将更新的文件复制到 *-model 工程下对应目录,静态资源复制方案1:maven 插件配置, 用于复制公共的资源pom.xml 增加插件配置

maven-resources-plugin

    
        copy-resources
        package
        
            copy-resources
        
        
            
                
                    ../../*-model/src/main/resources  
                      
                        templates/**
                        static/**
                    
                
            
            src/main/resources  
            true  
        
    

静态资源复制方案2:自定义的打包配置,增加资源复制推荐使用此方法,直接复制资源并打包到目标 zip 包路径:/src/main/assemble/package.xml,增加配置

  ../../*-model/src/main/resources
  \
    
      templates/**
      static/**
  

Java 工具类由于模板引擎对 JS 的支持有限,固增加 Java 工具类,用于模板中处理数据(模板引擎是在服务端执行,可执行 Java 代码)

参考 HtmlThymeleafHelper 配置, 注意 ModelAndView 中返回工具列后端开发 pom.xml 依赖

org.springframework.boot
spring-boot-starter-thymeleaf


org.springframework.boot
spring-boot-starter-web

开发模板


title



Hello Thymeleaf

张三(离线数据)


后端接口处理 ModelAndView// 接口返回 ModelAndView, 指定模板, 设置数据
@GetMapping("/template/render")
public ModelAndView templateReportDetail(HttpServletRequest request, @RequestParam String reportId) {

this.initJavaEnv(request);
return this.renderModelAndView("TemplateReportDetail", reportId);

}

// 通过 Session 设置 Java 对象,用于模板中执行 Java 方法
private void initJavaEnv(HttpServletRequest request) {

HtmlThymeleafHelper helper = Singleton.get(HtmlThymeleafHelper.class);
request.getSession().setAttribute("helper", helper);

}

// 创建一个模型视图对象
ModelAndView mav = new ModelAndView();
// 获取到查询的数据
Object data = ret.getRetObject();
// 将数据放置到ModelAndView对象view中,第二个参数可以是任何java类型
mav.addObject("sourceData", data);
// 放入模板路径
mav.setViewName("template");
// 返回ModelAndView对象mav
return mav;SpringBoot yml 配置spring:
mvc:

# 添加static文件夹下其他文件夹可访问
static-path-pattern: /project/static/**
# 自定义配置项,指定模板路径
base-template-path: /project/template

thymeleaf:

cache: true
mode: HTML5
suffix: .html
prefix: classpath:/templates/
encoding: UTF-8
servlet:
  content-type: text/html关于模板引擎 Thymeleaf什么是Thymeleaf?Thymeleaf 官网是这么解释的:Thymeleaf is a modern server-side Java template engine for both web and standalone environments.译过来就是:Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎什么是模板引擎?模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的html文档。从字面上理解模板引擎,最重要的就是模板二字,这个意思就是做好一个模板后套入对应位置的数据,最终以html的格式展示出来,这就是模板引擎的作用。不仅如此,在Java中模板引擎还有很多,模板引擎是动态网页发展进步的产物,在最初并且流传度最广的jsp它就是一个模板引擎。jsp是早期官方标准的模板,但是由于jsp的缺点比较多也挺严重的,所以很多人弃用jsp选用第三方的模板引擎,市面上开源的第三方的模板引擎也比较多,有Thymeleaf、FreeMaker、Velocity等模板引擎受众较广。听完了模板引擎的介绍,相信你也很容易明白了模板引擎在web领域的主要作用:让网站实现界面和数据分离,这样大大提高了开发效率,让代码重用更加容易。

PDF技术方案-wkhtmltopdf_第3张图片
Model、ModelMap、ModelAndViewModel 一般来说,可以用Model来接收各种类型的数据,如果使用来接收一组数据List,那么这个时候的Model实际上是ModelMapModelMap主要用于传递控制方法处理数据到结果页面,也就是说我们把结果页面上需要的数据放到ModelMap对象中即可,他的作用类似于request对象的setAttribute方法的作用:用来在一个请求过程中传递处理的数据 ModelMap或者Model通过addAttribute方法向页面传递参数ModelAndView 指模型和视图的集合,既包含 模型 又包含 视图Model和 ModelMap 无需用户自己创建,而且需要return 返回指定的页面路径Model和 ModelMap 无需用户自己创建,而且需要return 返回指定的页面路径

public String listCategory2(Model model) {

// 接收查询的信息
List cs2= categoryService.list();
// 封装了查询的数据
model.addAttribute("test", cs2);
//重要!!需要给出返回model跳转的路径
return "listCategory2";

}

ModelAndView的实例是需要我们手动new的,这也是和ModelMap的一个区别。
而且,ModelAndView 可以自己寻址,只需要return 返回其对象即可。

public ModelAndView listCategory(){
//创建一个模型视图对象

ModelAndView mav = new ModelAndView();
//获取到查询的数据
List cs= categoryService.list();

// //将数据放置到ModelAndView对象view中,第二个参数可以是任何java类型
mav.addObject("cs", cs);
// 放入jsp路径
mav.setViewName("listCategory");
 //返回ModelAndView对象mav
return mav;

}参考:https://cloud.tencent.com/developer/article/1698750Thymeleaf 常用标签标签作用示例th:id替换idth:text文本替换

bigsai

th:utext支持html的文本替换

content

th:object替换对象
th:value替换值th:each迭代th:href替换超链接超链接th:src替换资源七大基础对象:${#ctx} 上下文对象,可用于获取其它内置对象。${#vars}: 上下文变量。${#locale}:上下文区域设置。${#request}: HttpServletRequest对象。${#response}: HttpServletResponse对象。${#session}: HttpSession对象。${#servletContext}: ServletContext对象。常用的工具类:#strings:字符串工具类#lists:List 工具类#arrays:数组工具类#sets:Set 工具类#maps:常用Map方法。#objects:一般对象类,通常用来判断非空#bools:常用的布尔方法。#execInfo:获取页面模板的处理信息。#messages:在变量表达式中获取外部消息的方法,与使用#{...}语法获取的方法相同。#uris:转义部分URL / URI的方法。#conversions:用于执行已配置的转换服务的方法。#dates:时间操作和时间格式化等。#calendars:用于更复杂时间的格式化。#numbers:格式化数字对象的方法。#aggregates:在数组或集合上创建聚合的方法。#ids:处理可能重复的id属性的方法。引入css(必须要在标签中加上rel属性)

引入JavaScript:

超链接:
超链接

变量表达式: ${...}
在Thymeleaf中可以通过${…}进行取值,这点和ONGL表达式语法一致

取JavaBean对象:
使用${对象名.对象属性}或者${对象名['对象属性']}来取值
如果该JavaBean如果写了get方法,也可以通过get方法取值例如${对象.get方法名}



取List集合(each):
因为List集合是个有序列表,要遍历List对其中对象取值,而遍历需要用到标签:th:each,
具体使用为 ,其中item就相当于遍历每一次的对象名

直接取Map:
很多时候我们不存JavaBean而是将一些值放入Map中,再将Map存在Model中,我们就需要对Map取值,
可以 ${Map名['key']} 取值。也可以 ${Map名.key} 取值,当然也可以 ${map.get('key')}(java语法)取值

place:
feeling:
参考:https://developer.aliyun.com/article/769977Thymeleaf 控制处理 ? 会判断对象是否为空,如果为空就不会继续取值 SPEL处理 null 值 变量为 null 时,显示默认值 name?:'Unknown' 当 name 变量为 null 时,显示值 Unknown。等价于 name?name:'Unknown'。 对象为 null 时,避免调用方法或属性出错 placeOfBirth?.city 当 placeOfBirth 为 null 时,不再继续调用属性 city。 code?.toUpperCase() 当 code 为 null 时,不再继续调用方法 toUpperCase。 Map 获取的元素为 null 当 map 中没有名为 name 的元素时,这样写会报错 map.name。 安全的写法是这样:map['name']。 如果 map 中的元素为对象时,可以这样写:map['user']?.name。 List 类型数组越界 数组越界时,错误是这样的: Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "slist[2].score" (template: "exam/papers/edit" - line 117, col 92) Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1025E: The collection has '1' elements, index '2' is invalid SPEL 是这样的 slist[2].score,但 slist 不够3个元素(EL1025E: The collection has '1' elements, index '2' is invalid),因此数组越界了。 解决办法:添加数组大小的判断。上面的情况下,用 #lists.size(slist)>=3?slist[2]?.score:0 替换 slist[2].score参考:https://blog.csdn.net/sayyy/article/details/109385456参考:https://zhuanlan.zhihu.com/p/90642654Thymeleaf 调用 Java 方法1. Java 对象示例存入 Thymeleaf Context 域中,代码层面即为:将实例对象存入Request对象中 MethodService md = new MethodService(); mmap.put("methodService",md); mmap.put("proofsList",proofsList);

你可能感兴趣的:(PDF技术方案-wkhtmltopdf)