SpringBoot+Freemark渲染html导出PDF
摘要
PDF 导出在需要将信息纸质化存档时会使用到。这里将介绍在 spring boot 框架下使用 Freemarker + iText 将 ftl 模板渲染成 html,然后导出 PDF 文件
Freemarker 官方文档: https://freemarker.apache.org/docs/index.html
Maven依赖
org.springframework.boot
spring-boot-starter-freemarker
com.itextpdf
html2pdf
4.0.3
核心代码
@GetMapping(value = "/export-pdf")
public ResponseEntity exportPDF(TopoDeviceQueryDto queryDto) {
ResponseEntity response = null;
try {
HashMap mapData = Maps.newHashMap();
mapData.put("topDeviceList", topoDeviceService.findByList(queryDto));
String templateContent = HtmlUtils.getTemplateContent("TopoDevice.ftl", mapData);
response = this.getDownloadResponse(HtmlUtils.html2Pdf(templateContent), "device info.pdf");
} catch (Exception e) {
log.error("error occurs when downloading file");
response = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return response;
}
@Slf4j
public class HtmlUtils {
/**
* @return
* @throws Exception
*/
public static String getTemplateDirectory() {
ClassLoader classLoader = HtmlUtils.class.getClassLoader();
URL resource = classLoader.getResource("templates");
try {
return Objects.requireNonNull(resource).toURI().getPath();
} catch (URISyntaxException e) {
log.error("获取模板文件夹失败,{}", e);
}
return null;
}
/**
* 获取模板内容
*
* @param templateName 模板文件名
* @param paramMap 模板参数
* @return
* @throws Exception
*/
public static String getTemplateContent(String templateName, Map paramMap) throws Exception {
Configuration config = ApplicationContextUtil.getBean(FreeMarkerConfigurer.class).getConfiguration();
config.setDefaultEncoding("UTF-8");
Template template = config.getTemplate(templateName, "UTF-8");
return FreeMarkerTemplateUtils.processTemplateIntoString(template, paramMap);
}
/**
* HTML 转 PDF
*
* @param content html内容
* @param outPath 输出pdf路径
* @return 是否创建成功
*/
public static boolean html2Pdf(String content, String outPath) {
try {
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
FontProvider fontProvider = new FontProvider();
fontProvider.addSystemFonts();
converterProperties.setFontProvider(fontProvider);
HtmlConverter.convertToPdf(content, new FileOutputStream(outPath), converterProperties);
} catch (Exception e) {
log.error("生成模板内容失败,{}", e);
return false;
}
return true;
}
/**
* HTML 转 PDF
*
* @param content html内容
* @return PDF字节数组
*/
public static ByteArrayOutputStream html2Pdf(String content) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
FontProvider fontProvider = new FontProvider();
fontProvider.addSystemFonts();
converterProperties.setFontProvider(fontProvider);
HtmlConverter.convertToPdf(content, outputStream, converterProperties);
} catch (Exception e) {
log.error("生成 PDF 失败,{}", e);
}
return outputStream;
}
}
@Configuration
@AutoConfigureOrder(-1)
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static Object getBeanByName(String beanName) {
if (applicationContext == null) {
return null;
}
return applicationContext.getBean(beanName);
}
public static T getBean(Class type) {
return applicationContext.getBean(type);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
}
网络拓扑结构表
网络拓扑结构表
设备名称
符号
端口
地址
网络中的单元
<#if topDeviceList??>
<#list topDeviceList as top_device>
<#if top_device.deviceName??>${top_device.deviceName}#if>
<#if top_device.mark??>${top_device.mark}#if>
<#if top_device.port??>${top_device.port}#if>
<#if top_device.ip??>${top_device.ip}#if>
<#if top_device.unit??>${top_device.unit}#if>
#list>
#if>
踩坑记录
解决Spring项目打成Jar包后Freemarker找不到模板的问题
freemarker在jar包中无法使用类加载器获取resourse目录下的templates文件,出现的问题代码如下:(本地测试正常,打包jar后无法获取模板)
@GetMapping(value = "/export-pdf")
public ResponseEntity exportPDF(TopoDeviceQueryDto queryDto) {
ResponseEntity response = null;
try {
HashMap mapData = Maps.newHashMap();
mapData.put("topDeviceList", topoDeviceService.findByList(queryDto));
String templateContent = HtmlUtils.getTemplateContent("TopoDevice.ftl", mapData);
response = this.getDownloadResponse(HtmlUtils.html2Pdf(templateContent), "device info.pdf");
} catch (Exception e) {
log.error("error occurs when downloading file");
response = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return response;
}
public static String getTemplateDirectory() {
ClassLoader classLoader = HtmlUtils.class.getClassLoader();
URL resource = classLoader.getResource("templates");
try {
return Objects.requireNonNull(resource).toURI().getPath();
} catch (URISyntaxException e) {
log.error("获取模板文件夹失败,{}", e);
}
return null;
}
/**
* 获取模板内容
*
* @param templateDirectory 模板文件夹
* @param templateName 模板文件名
* @param paramMap 模板参数
* @return
* @throws Exception
*/
public static String getTemplateContent(String templateDirectory, String templateName, Map paramMap) throws Exception {
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
try {
configuration.setDirectoryForTemplateLoading(new File(templateDirectory));
} catch (Exception e) {
System.out.println("-- exception --");
}
Writer out = new StringWriter();
Template template = configuration.getTemplate(templateName, "UTF-8");
template.process(paramMap, out);
out.flush();
out.close();
return out.toString();
}
/**
* HTML 转 PDF
*
* @param content html内容
* @return PDF字节数组
*/
public static ByteArrayOutputStream html2Pdf(String content) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
FontProvider fontProvider = new FontProvider();
fontProvider.addSystemFonts();
converterProperties.setFontProvider(fontProvider);
HtmlConverter.convertToPdf(content, outputStream, converterProperties);
} catch (Exception e) {
log.error("生成 PDF 失败,{}", e);
}
return outputStream;
}
修改后的代码(打包jar后正常获取模板信息)
@GetMapping(value = "/export-pdf")
public ResponseEntity exportPDF(TopoDeviceQueryDto queryDto) {
ResponseEntity response = null;
try {
HashMap mapData = Maps.newHashMap();
mapData.put("topDeviceList", topoDeviceService.findByList(queryDto));
String templateContent = HtmlUtils.getTemplateContent("TopoDevice.ftl", mapData);
response = this.getDownloadResponse(HtmlUtils.html2Pdf(templateContent), "device info.pdf");
} catch (Exception e) {
log.error("error occurs when downloading file");
response = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return response;
}
/**
* 获取模板内容
*
* @param templateName 模板文件名
* @param paramMap 模板参数
* @return
* @throws Exception
*/
public static String getTemplateContent(String templateName, Map paramMap) throws Exception {
Configuration config = ApplicationContextUtil.getBean(FreeMarkerConfigurer.class).getConfiguration();
config.setDefaultEncoding("UTF-8");
Template template = config.getTemplate(templateName, "UTF-8");
return FreeMarkerTemplateUtils.processTemplateIntoString(template, paramMap);
}
public static T getBean(Class type) {
return applicationContext.getBean(type);
}
/**
* HTML 转 PDF
*
* @param content html内容
* @return PDF字节数组
*/
public static ByteArrayOutputStream html2Pdf(String content) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
FontProvider fontProvider = new FontProvider();
fontProvider.addSystemFonts();
converterProperties.setFontProvider(fontProvider);
HtmlConverter.convertToPdf(content, outputStream, converterProperties);
} catch (Exception e) {
log.error("生成 PDF 失败,{}", e);
}
return outputStream;
}
ps:.ftl模板文件放在 templates 目录下
总结:freemarker无法使用类加载器获取jar包中的resourse目录下的templates文件
解决办法:注入FreeMarkerConfigurer
配置类,因为freemarker模板的默认目录就在resourse下的templates目录下,
使用freeMarkerConfigurer.getConfiguration().getTemplate("mailTemplate.ftl")
可直接获取到对应的模板文件
解决Freemark导出Pdf部署Linux乱码问题
项目部署完之后发现Linux下个别字体丢失的问题,刚开始以为是编码问题,经排查是Linux系统中文字体缺失问题
第一步:拷贝Windows下系统的中文字体 Windows下字体的目录是在C:\Windows\Fonts
第二步:移动字体库到linux系统下的字体库文件夹/usr/share/fonts/下
第三步:让linux系统识别新的中文字体:终端输入:sudo fc-cache -fv