SpringBoot手把手實現(xiàn)生成PDF功能的示例代碼
本文介紹了如何使用Thymeleaf模板和iText庫配合Flying-pdf工具,實現(xiàn)在業(yè)務系統(tǒng)中生成PDF文件的功能,以訂單和發(fā)貨單為例,詳細講解了字體配置、Maven依賴、工具類實現(xiàn)和控制層方法。
需求:
在開發(fā)的業(yè)務系統(tǒng)中我希望能有一個轉出PDF功能。比如:轉出訂單,轉出發(fā)貨單等
原理:
使用itext 與flying-pdf 對 thymeleaf 模板填充后生成PDF
實現(xiàn):
1.準備中文字體,用于PDF中關于中文文字的顯示。
存放位置

2.Maven 主要依賴
<!--itext 生成PDF-->
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>${itext.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<!--xlsx(07)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.9</version>
</dependency>3.工具類
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.xml.xmp.XmpWriter;
import lombok.extern.slf4j.Slf4j;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.util.List;
import java.util.Map;
/**
* pdf處理工具類
*
*/
@Slf4j
public class PdfUtil {
/**
* 以文件流形式下載到瀏覽器
*
* @param templateEngine 配置
* @param templateName 模板名稱
* @param listVars 模板參數(shù)集
* @param response HttpServletResponse
* @param fileName 下載文件名稱
*/
public static void download(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response, String fileName) {
// 設置編碼、文件ContentType類型、文件頭、下載文件名
response.setCharacterEncoding(XmpWriter.UTF8);
response.setContentType("application/pdf");
try {
response.setHeader("Content-Disposition", "attachment;fileName=" +
new String(fileName.getBytes("gb2312"), "ISO8859-1"));
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage(), e);
}
try (ServletOutputStream out = response.getOutputStream()) {
generateAll(templateEngine, templateName, out, listVars);
out.flush();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* pdf下載到特定位置
*
* @param templateEngine 配置
* @param templateName 模板名稱
* @param listVars 模板參數(shù)集
* @param filePath 下載文件路徑
*/
public static void save(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, String filePath) {
try (OutputStream out = new FileOutputStream(filePath);) {
generateAll(templateEngine, templateName, out, listVars);
out.flush();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* pdf預覽
*
* @param templateEngine 配置
* @param templateName 模板名稱
* @param listVars 模板參數(shù)集
* @param response HttpServletResponse
*/
public static void preview(TemplateEngine templateEngine, String templateName, List<Map<String, Object>> listVars, HttpServletResponse response) {
try (ServletOutputStream out = response.getOutputStream()) {
generateAll(templateEngine, templateName, out, listVars);
out.flush();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* 按模板和參數(shù)生成html字符串,再轉換為flying-saucer識別的Document
*
* @param templateName 模板名稱
* @param variables 模板參數(shù)
* @return Document
*/
private static Document generateDoc(TemplateEngine templateEngine, String templateName, Map<String, Object> variables) {
// 聲明一個上下文對象,里面放入要存到模板里面的數(shù)據(jù)
final Context context = new Context();
context.setVariables(variables);
StringWriter stringWriter = new StringWriter();
try(BufferedWriter writer = new BufferedWriter(stringWriter)) {
templateEngine.process(templateName,context, writer);
writer.flush();
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
return builder.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
}catch (Exception e){
//ResponseEnum.TEMPLATE_PARSE_ERROR.assertFail(e);
}
return null;
}
/**
* 核心: 根據(jù)Thymeleaf 模板生成pdf文檔
*
* @param templateEngine 配置
* @param templateName 模板名稱
* @param out 輸出流
* @param listVars 模板參數(shù)
* @throws Exception 模板無法找到、模板語法錯誤、IO異常
*/
private static void generateAll(TemplateEngine templateEngine, String templateName, OutputStream out, List<Map<String, Object>> listVars) throws Exception {
// 斷言參數(shù)不為空
//ResponseEnum.TEMPLATE_DATA_NULL.assertNotEmpty(listVars);
ITextRenderer renderer = new ITextRenderer();
//設置字符集(宋體),此處必須與模板中的<body style="font-family: SimSun">一致,區(qū)分大小寫,不能寫成漢字"宋體"
ITextFontResolver fontResolver = renderer.getFontResolver();
//避免中文為空設置系統(tǒng)字體
fontResolver.addFont("static/fonts/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 如果linux下有問題,可使用以下方式解決。
// 有一個項目是用docker部署的,一直報錯找不到simsun.ttf文件,但需要將simsun.ttf上傳到/usr/share/fonts
//fontResolver.addFont(CommonUtil.isLinux() ? "/usr/share/fonts/simsun.ttf" : "static/fonts/simsun.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//根據(jù)參數(shù)集個數(shù)循環(huán)調用模板,追加到同一個pdf文檔中
//(注意:此處從1開始,因為第0是創(chuàng)建pdf,從1往后則向pdf中追加內容)
for (int i = 0; i < listVars.size(); i++) {
Document docAppend = generateDoc(templateEngine, templateName, listVars.get(i));
renderer.setDocument(docAppend, null);
//展現(xiàn)和輸出pdf
renderer.layout();
if(i==0){
renderer.createPDF(out, false);
}else {
//寫下一個pdf頁面
renderer.writeNextDocument();
}
}
renderer.finishPDF(); //完成pdf寫入
}
}4.控制層方法 (原理是把對象傳過去,由thymeleaf解析)
import com.flo.po.Dto.OrderDetailedDto;
import com.flo.po.Dto.OrderDto;
import com.flo.service.OrderDetailedService;
import com.flo.service.OrderService;
import com.flo.util.PdfUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.thymeleaf.TemplateEngine;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文檔預覽、下載
*
*/
@RestController
@RequestMapping(value = "/document")
@Api(tags = "文檔預覽、下載API")
public class DocumentController {
@Autowired
private TemplateEngine templateEngine;
@Autowired
private OrderService orderService;//訂單表頭
@Autowired
private OrderDetailedService orderDetailedService;//訂單表身
/**
* pdf預覽
* @param response HttpServletResponse
*/
@GetMapping(value = "/pdf/preview")
@ApiOperation(value="pdf預覽")
public void preview(HttpServletResponse response,Integer id) {
OrderDto orderDto=orderService.findById(id);
List<OrderDetailedDto> list=orderDetailedService.findAll(new OrderDetailedDto(){{setOrderId(id);}});
// 構造freemarker模板引擎參數(shù),listVars.size()個數(shù)對應pdf頁數(shù)
List<Map<String,Object>> listVars = new ArrayList<>();
Map<String,Object> variables = new HashMap<>(4);
variables.put("title","訂單明細");
variables.put("custName",orderDto.getCustomName());
variables.put("adr",orderDto.getAddress());
variables.put("tel",orderDto.getTel());
variables.put("list",list);
listVars.add(variables);
PdfUtil.preview(templateEngine,"pdfPage",listVars,response);
}
}5.PDF模板(pdfPage.html)
注意事項:
1.盡可能把CSS樣式放在這個模板里,外部引入可能會存在一些問題。
2.關于圖片,切記一定要給完整路徑,而非相對路徑。如./uploadFile/logo.png 需要加上http://地址/uploadFile/logo.png
3.圖片名稱不能包含中文,否則會找不到路徑(是由于編碼為UTF8的原因)
位置

效果:


到此這篇關于SpringBoot手把手實現(xiàn)生成PDF功能的示例代碼的文章就介紹到這了,更多相關SpringBoot PDF內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MyBatis-Plus多數(shù)據(jù)源配置與讀寫分離全過程
文章介紹了如何在SpringBoot中使用MyBatis-Plus實現(xiàn)多數(shù)據(jù)庫操作,包括純粹多庫和讀寫分離的配置方法,重點演示了多數(shù)據(jù)源的設置與使用2025-09-09
idea報錯java:?非法字符:?‘\ufeff‘的解決步驟以及說明
這篇文章主要介紹了idea報錯java:非法字符:\ufeff的解決步驟以及說明,文章詳細解釋了為什么在Java中會出現(xiàn)\ufeff錯誤,這是由于BOM字符被認為是非法字符導致的,通過示例代碼將解決的過程介紹的非常詳細,需要的朋友可以參考下2025-05-05
java 通過聚合查詢實現(xiàn)elasticsearch的group by后的數(shù)量
這篇文章主要介紹了java 通過聚合查詢實現(xiàn)elasticsearch的group by后的數(shù)量,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12

