springboot 动态导出pdf
原理是通过freemarker插入数据到html模版里面,html你就叫前端帮你写好,他不写你就告诉你主管让他叫他写,你只管往预留的位置插入数据就行,然后通过itextpdf会将准备好html文件转成PDF。
### 1、准备依赖
<!--下载pdf begin--><dependency><groupId>com.itextpdf</groupId><artifactId>html2pdf</artifactId><version>4.0.3</version></dependency><!-- freemarker模版 --><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.30</version></dependency><!--下载pdf end-->
### 2、准备html模版
<html>
<head><meta charset="UTF-8"><style>.main-container {font-family: SF-UI-Text-Regular;width: 21cm;margin: 0 auto;padding: 20px;}.float-left {float: left;}.float-right {float: right;}.padding-left-right {padding-left: 20px;padding-right: 20px;}.margin-left-right {margin-left: 20px;margin-right: 20px;}.section {/*border: 3px dashed #CCC;*//*padding: 20px;*/border-Top: 2px dashed #D3D3D3;padding-Top: 13px;margin-bottom: 16px;margin-Top: 20px;}.section-details p {color: #126089;/*font-size: 16px;*/}.grouper {overflow: hidden;}.grid-50 {width: 50%;}@font-face {font-family: 'SF-UI-Text-Regular';src: url("https://s3-ap-southeast-1.amazonaws.com/content.staging.whitecoat.global/PDF/Templates/fonts/SF-UI-Text-Regular.otf");font-style: normal;}h1 {font-size: 22px;font-family: 'SF-UI-Text-Regular';}.h1-main {margin-top: 90px;}p {margin: 0 0 10px 0;font-family: 'SF-UI-Text-Regular';font-size: 12px;}.p-spec {color: #149a9a;font-weight: bold;}table, th, td {padding: 5px;border-collapse: collapse;}th, td {border: 1px solid #D3D3D3;}.margin-b-30 {margin-bottom: 30px;}.row:after {content: "";display: table;clear: both;}.column-4 {float: left;width: 33.33%;padding: 10px;}.column-8 {float: left;width: 66.66%;padding: 10px;}* {box-sizing: border-box;font-size: 12px;}</style>
</head>
<body><div class="main-container"><div style="background-color: #fff"><div class="grouper main-header grouper padding-left-right" style="padding-top: 20px"><div class="margin-b-30 row"><div class="column-4"><img style="max-width:90%"src="https://s3-ap-southeast-1.amazonaws.com/content.staging.whitecoat.global/PDF/Templates/Images/WhiteCoat_Tax_Invoice.png"></div><div class="column-4">WhiteCoat Holdings Pte Ltd. <br/>Clinic Licence No.: 18M0119/01/182 <br/>201 Henderson Road #05-11/12 <br/>Apex @ Henderson <br/>Singapore 159545 <br/></div><div class="column-4">GST Reg. No: 201610235E <br/>T: +65 6909 6909 <br/>F: +65 6909 6909 <br/>E: contactus@whitecoat.com.sg <br/><a style="text-decoration: none;" href="https://whitecoat.com.sg/">whitecoat.com.sg</a></div></div></div><div class="main-body"><div class="grouper"><div class="grouper margin-left-right row" style="border-Top: 1px solid #D3D3D3;padding-top:20px;"><div class="column-4">Name: <br/>${data.name!} <br/><br/>NRIC/FIN/Passport No: <br/>${data.idNo!}</div><div class="column-8">Invoice No.: <br/>${data.invoiceNo!} <br/><br/>Invoice Date: <br/>${data.invoiceDateStr!} <br/><br/><i>This is a computer generated invoice. No signature is required.</i></div></div><div class="grouper margin-left-right" style="border-Top: 1px solid #D3D3D3;padding-top:20px;"><table><tr><th>S/N</th><th colspan="2">Description</th><th style="border:1px solid #D3D3D3;">Amount</th></tr><tr><td style="width:30px;text-align:center;">1</td><td style="width:550px; border-right: none">Medication <br/><#list data.medicationList as item>${item.productName!} <br/></#list><i><br/>Subtotal</i></td><td style="text-align:right; border-left: none">Quantity <br/><#list data.medicationList as item>${item.quantity!} <br/></#list><i style="color:#fff"><br/>Subtotal</i></td><td style="width:200px;text-align:right"><br/><#list data.medicationList as item>$${item.amount!} <br/></#list><br/><i>${data.subtotal!}</i></td></tr><tr><td style="text-align:center;">2</td><td colspan="2">Administrative charges</td><td style="text-align:right">$${data.adminCharge!}</td></tr><tr><td colspan="3" style="text-align:right; border-bottom: none; border-left: none">Subtotal(Excluding GST)</td><td style="text-align:right">$${data.subtotalExcludingGST!}</td></tr><tr><td colspan="3"style="text-align:right; ; border-bottom: none; border-top: none; border-left: none">GST(${data.tax!}%)</td><td style="text-align:right">$${data.GST!}</td></tr><tr><td colspan="3"style="text-align:right; border-top: none; border-left: none; border-bottom: none">Total(Including GST)</td><td style="text-align:right">$${data.totalIncludingGST!}</td></tr></table></div><div class="grouper margin-left-right float-left" style="padding-bottom :20px;padding-top :20px"><span>Bank remittance details for payment by Bank Telegraphic <br/>Transfer in Singapore Dollars to:<br/>Name of Bank: United Overseas Bank Ltd. Holland Branch<br/>Account Name: Whitecoat Holdings Pte Ltd.<br/>Account No: 341-308-316-6<br/>Bank Code: 7375<br/>Branch Code: 020<br/>Bank Swift Code: UOVBSGSG</span></div></div></div></div></body>
</html>
### 3、下载代码
@ApiOperation(value = "下载pdf")@GetMapping("downloadPDF/staffPurchase")public ResponseResult download(String id, HttpServletResponse response) throws Exception {StaffPurchasePDFVo data = staffPurchaseMapper.getStaffPurchasePDFData(id);HashMap<String, Object> mapData = Maps.newHashMap();mapData.put("data", data);long timeMillis = System.currentTimeMillis();String templateContent = HtmlUtils.getTemplateContent("Staff_Purchase_Receipt.ftl", mapData);HtmlUtils.html2Pdf(response, templateContent, timeMillis + "");return ResponseResult.success();}
### 4、htmlutils代码
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.layout.font.FontProvider;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;
import java.util.Objects;@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<String, Object> 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 void html2Pdf(HttpServletResponse response, String content,String filename) throws IOException {response.setHeader("Content-Type", "application/octet-stream");response.setCharacterEncoding("utf-8");response.setHeader("Content-Disposition", "attachment;filename="+filename+".pdf");try (ServletOutputStream outputStream = response.getOutputStream()) {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);}}
}
ApplicationContextUtil代码、
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;@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> T getBean(Class<T> type) {return applicationContext.getBean(type);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {ApplicationContextUtil.applicationContext = applicationContext;}}
5、freemarker的基本语法
https://developer.aliyun.com/article/942606