JAVA后端导出PDF文件
前言
效果图:
一、前端代码
// 直接用href请求后端地址即可
window.location.href = http://localhost:8989/exportPdf;
二、使用步骤
1. ftl模板
注意:贴出的接口代码,读取的ftl模板的路径是,src\main\resources\templates\free.ftl
<#-- 如果值为null/空,则设置为空值 -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Freemarker</title>
<style type="text/css">
#all {
width: 600px;
<#-- 使文档在pdf页面居中 --> margin: auto;
}
table {
width: 100%;
height: auto;
<#-- @@提醒@@:此处必须指定字体,不然不识别中文 --> font-family: SimSun;
text-align: center;
<#-- table中单元格自动换行 --> table-layout: fixed;
word-wrap: break-word;
}
tr td {
padding: 2px;
font-size: 10px;
height: 20px;
}
tr th {
padding: 2px;
font-weight: bold;
font-size: 10px;
height: 20px;
}
</style>
</head>
<body>
<div id="all">
<table border="1" cellspacing="0" cellpadding="0" align="center">
<tr>
<th colspan="5">${title}</th>
</tr>
<tr>
<#-- 动态获取params的大小size -->
<th rowspan="${params?size+1}">基本信息</th>
<td>姓名</td>
<td>电话</td>
<td>昵称</td>
<td>邮箱</td>
</tr>
<#list params as param>
<tr>
<td>${param.name}</td>
<td>${param.phone}</td>
<td>${param.nickname}</td>
<td>${param.email}</td>
</tr>
</#list>
</table>
</div>
</body>
</html>
2.后端代码
依赖:
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.0.3</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.0</version>
</dependency>
2.1 核心接口代码
package com.example.export;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 导出pdf
*
*/
@Slf4j
@RestController
public class ExportPdf {
@GetMapping("/exportPdf")
public void exportPdf(HttpServletResponse response) {
// ========== pdf处理方式 ===========
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "赵小刚");
map1.put("phone", "15673890987");
map1.put("nickname", "红孩儿");
map1.put("email", "15673890987@163.com");
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "赵小刚");
map2.put("phone", "15673890987");
map2.put("nickname", "红孩儿");
map2.put("email", "15673890987@163.com");
List<Map<String, Object>> stuffList = Arrays.asList(map1,map2);
Map<String, Object> stuffMap = new HashMap<>();
stuffMap.put("params", stuffList);
stuffMap.put("title", "测试PDF模板");
File pdfFile = FileUtil.file("test.pdf");
try (OutputStream os = new FileOutputStream(pdfFile);) {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
// 如果填充值中与模板不匹配,或者为null值,则默认此值为空;
cfg.setClassicCompatible(true);
// 模板路径加载方式:基于类路径,即在src\main\resources\templates包下的ftl模板
cfg.setClassForTemplateLoading(this.getClass(),"/templates");
Template template = cfg.getTemplate( "pdf.ftl");
Writer writer = new StringWriter();
template.process(stuffMap, writer);
// 对pdf资料进行创建
HtmlToPdf.htmlToPdf(writer.toString(),os);
byte[] dataByte = FileUtil.readBytes(pdfFile);
response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(dataByte.length));
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s"
, URLEncoder.encode("pdf测试.pdf", "UTF-8")));
IoUtil.write(response.getOutputStream(), Boolean.TRUE, dataByte);
} catch (Exception e) {
log.error("导出贷后检查表失败, 详情:{}", e.getMessage());
} finally {
// 删除项目classes/目录下生成的临时文件
if (pdfFile.exists()) {
FileUtil.del(pdfFile);
}
}
}
}
2.2 本地方法 HtmlToPdf.htmlToPdf()
package com.example.export;
import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.pdf.BaseFont;
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 org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.OutputStream;
/**
* 将HTML转成PD格式的文件。html文件的格式比较严格
* @param htmlText html内容
* @param os pdf存储文件输出流, 需要调用方关闭流
* @throws Exception
*/
public static void htmlToPdf(String htmlText, OutputStream os) throws Exception {
// <!DOCTYPE html> 没有闭合标签,故删掉
htmlText = htmlText.replace("<!DOCTYPE html>", StrUtil.EMPTY);
Document document = Jsoup.parse(htmlText);
/*手动为一些标签添加闭合*/
Elements meta = document.getElementsByTag("meta");
String html = document.html();
for (Element link : meta) {
String s = link.outerHtml();
String s1 = s.replace("/>", ">");
html = html.replace(s, s1 + "</meta>");
}
Elements link1 = document.getElementsByTag("link");
for (Element link : link1) {
String s = link.outerHtml();
String s1 = s.replace("/>", ">");
html = html.replace(s, s1 + "</link>");
}
html = html.replaceAll("<br>", "<br/>");
html = html.replaceAll(" ", " ");
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(html);
// 解决中文支持, 注意本地和生产必须有此字体文件
ITextFontResolver fontResolver = renderer.getFontResolver();
String currentSystem = System.getProperty("os.name").toLowerCase();
if("linux".equals(currentSystem)){
fontResolver.addFont("/usr/share/fonts/chinese/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}else{
fontResolver.addFont("C:/Windows/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
}
renderer.layout();
renderer.createPDF(os);
}
3.测试
启动项目,使用浏览器请求:http://localhost:8989/exportPdf
即可实现导出pdf测试文件
(本文完!转载需说明出处!)