当前位置: 首页 > news >正文

Java基于itextPDF实现pdf动态导出

Java基于itextPDF实现pdf动态导出

  • 1、制作PDF导出模板
  • 2 、集成itextpdf
  • 3 、编写实体
  • 4 、编写主要代码
  • 5、编写controller并测试
    • 补充:踩坑记录

现在的业务越来越复杂了,有些业务场景已经不能满足与EXCEL导出和WORD导出了,例如准考证打印,电子证书等等,这些都是动态数据导出的PDF。接下来我们就看一下怎么实现PDF的动态导出吧。

1、制作PDF导出模板

第一步,我们需要制作一个PDF模板,可以先使用WORD去制作,制作完成以后再转为PDF。
在这里插入图片描述在这里插入图片描述
当转为PDF以后,我们就需要去给PDF设置表单域了,表单域的名称和你要填充的数据名称需要一一对应。
在这里插入图片描述

这里推荐几个可以编辑表单域的软件:Adobe Acrobat 、 万兴PDF、PDFill、Nitro
我这里懒省事用的万兴PDF(免费版有水印),具体哪个更好用一点请大家自行判断。

2 、集成itextpdf

接下来第二步则是在项目中集成itextpdf,项目中使用的是SpringBoot 2.7 , 同时还集成了lombok.

<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version>
</dependency>

3 、编写实体

编写导出PDF需要用到的实体,这里注意,实体中的属性名需要和表单域名一一对应。
同时为了方便测试,在无参构造中初始化了一些默认数据。

package com.vinci.pdf.entity;import lombok.Data;/*** @package: com.vinci.pdf.entity* @className: Person* @author: Vinci* @description: 测试用实体* @date: 2023/11/13 9:56*/
@Data
public class Person {/*** @description: 姓名**/private String name;/*** @description: 国籍**/private String nationality;/*** @description: 居住地**/private String address;/*** @description: 民族**/private String nation;/*** @description: 户籍地**/private String registeredResidence;/*** @description: 身高 / 体重**/private String heightAndWeight;/*** @description: 婚姻状况**/private String maritalStatus;/*** @description: 年龄**/private Integer age;/*** @description: 照片**/private String largeHeadPhoto;/*** @description: 这里为了方便测试,在无参构造直接初始化数据来模拟持久化数据。**/public Person() {this.name = "vinci";this.nationality = "中国";this.address = "江苏南京";this.nation = "汉族";this.registeredResidence = "河南漯河";this.heightAndWeight = "178cm / 65Kg";this.maritalStatus = "未婚";this.age = 24;this.largeHeadPhoto = Thread.currentThread().getContextClassLoader().getResource("static/header1.jpg").getFile();}}

4 、编写主要代码

在Service实现类中编写主要功能,将数据填充到PDF中并实现导出。

package com.vinci.pdf.service.impl;import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.vinci.pdf.entity.Person;
import com.vinci.pdf.service.api.PdfGenerateTestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Objects;/*** @package: com.vinci.pdf.service.impl* @className: PdfGenerateTestServiceImpl* @author: Vinci* @description: pdf生成测试接口实现* @date: 2023/11/13 10:15*/
@Service
public class PdfGenerateTestServiceImpl implements PdfGenerateTestService {/*** @description: 日志服务**/private static final Logger log = LoggerFactory.getLogger(PdfGenerateTestServiceImpl.class);/*** @description: pdf生成* @author: Vinci* @date: 2023/11/13 10:25**/@Overridepublic void pdfGenerate(HttpServletResponse response) throws UnsupportedEncodingException {// 模板地址URL resource = Thread.currentThread().getContextClassLoader().getResource("templates/aipuu-y1mhx.pdf");if(resource == null){throw new RuntimeException("没有找到模板");}String path = resource.getPath();// PDF的文件名称 及响应头String fileName = "test.pdf";fileName = URLEncoder.encode(fileName, "UTF-8");response.setContentType("application/force-download");//如果想要下载文件的话,这里的inline可以替换为 attachmentresponse.setHeader("Content-Disposition","fileName=" + fileName);OutputStream ops = null;ByteArrayOutputStream bos = null;PdfStamper pdfStamper = null;PdfReader pdfReader = null;try {ops = response.getOutputStream();pdfReader = new PdfReader(path);bos = new ByteArrayOutputStream();// 根据模板生成新的PDFpdfStamper = new PdfStamper(pdfReader, bos);AcroFields form = pdfStamper.getAcroFields();// 设置字体BaseFont font = BaseFont.createFont("C:/WINDOWS/Fonts/SIMSUN.TTC,1",BaseFont.IDENTITY_H,BaseFont.EMBEDDED);form.addSubstitutionFont(font);// 获取数据(这里在无参构造中生成了一些数据,实际开发中可用持久化数据来代替)Person person = new Person();// 通过反射遍历来给PDF中的表单生成数据Field[] fields = person.getClass().getDeclaredFields();for (Field field : fields) {field.setAccessible(true);String key = field.getName();Object value = field.get(person);if(!Objects.equals(key,"largeHeadPhoto")){//处理文本数据form.setField(key, value.toString());}else{// 通过表单域名获取所在页和坐标,左下角为起点int pageNo = form.getFieldPositions(key).get(0).page;Rectangle signRect = form.getFieldPositions(key).get(0).position;float x = signRect.getLeft();float y = signRect.getBottom();// 读图片Image image = Image.getInstance(value.toString());// 获取操作的页面PdfContentByte under = pdfStamper.getOverContent(pageNo);// 根据域的大小缩放图片image.scaleToFit(signRect.getWidth(), signRect.getHeight());// 添加图片image.setAbsolutePosition(x, y);under.addImage(image);}}// 设置PDF为只读pdfStamper.setFormFlattening(true);// 关闭资源pdfStamper.close();Document doc = new Document();PdfCopy copy = new PdfCopy(doc, ops);doc.open();PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);copy.addPage(importPage);doc.close();}catch (Exception e){log.error("发现异常",e);}finally {try {if (ops != null) {ops.flush();ops.close();}if (pdfReader != null) {pdfReader.close();}}catch (Exception e){log.error("发现异常",e);}}}}

5、编写controller并测试

编写Controller来方便我们通过浏览器的请求的方式去测试

package com.vinci.pdf.controller;import com.vinci.pdf.service.api.PdfGenerateTestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;/*** @package: com.vinci.pdf.controller* @className: PdfGenerateTestController* @author: Vinci* @description:pdf生成测试controller* @date: 2023/11/13 10:16*/
@RestController
@RequestMapping("/pdf")
public class PdfGenerateTestController {/*** @description: 日志打印**/private static final Logger log = LoggerFactory.getLogger(PdfGenerateTestController.class);/*** @description: 业务接口**/@Resourceprivate PdfGenerateTestService pdfGenerateTestService;/*** @description: 测试pdf生成* @author: Vinci* @date: 2023/11/13 10:17**/@GetMapping(value = "/generate")public void pdfGenerate(HttpServletResponse response){try{pdfGenerateTestService.pdfGenerate(response);}catch (Exception e){log.error("发现异常",e);}}
}

这里我们打开浏览器访问 http://localhost:8080/pdf/generate 发现PDF已经在下载了
在这里插入图片描述
下载成功后我们打开,发现里面已经有数据了。
在这里插入图片描述

补充:踩坑记录

使用万兴PDF编辑图片类型的表单域时一定要注意,去掉背景色,否则导出后你会看不到图片
在这里插入图片描述

本文代码下载地址:https://gitee.com/vinci99/springboot-pdf-generate.git

相关文章:

Java基于itextPDF实现pdf动态导出

Java基于itextPDF实现pdf动态导出 1、制作PDF导出模板2 、集成itextpdf3 、编写实体4 、编写主要代码5、编写controller并测试补充&#xff1a;踩坑记录 现在的业务越来越复杂了&#xff0c;有些业务场景已经不能满足与EXCEL导出和WORD导出了&#xff0c;例如准考证打印&#x…...

【Liunx】配置IP地址与MAC地址绑定

配置IP地址与MAC地址绑定 A.查询MAC地址B.绑定前的准备1.资源&#xff1a;(1) 服务器Server1:192.168.122.1(2) 服务器Server1:192.168.122.2 2. Server1按照dhcp服务 C.开始绑定操作1.修改dhcp配置文件2.生效 A.查询MAC地址 点击这里查看【如何查询服务器IP与MAC地址】 B.绑定…...

Mybatis-Plus最新教程

目录 原理&#xff1a;MybatisPlus通过扫描实体类&#xff0c;并基于反射获取实体类信息作为数据库信息。 ​编辑1.添加依赖 2.常用注解 3.常见配置&#xff1a; 4.条件构造器 5.QueryWrapper 6.UpdateWrapper 7.LambdaQueryWrapper:避免硬编码 8.自定义SQL 9.Iservic…...

【Shell脚本11】Shell 函数

Shell 函数 linux shell 可以用户定义函数&#xff0c;然后在shell脚本中可以随便调用。 shell中函数的定义格式如下&#xff1a; [ function ] funname [()]{action;[return int;]}说明&#xff1a; 1、可以带function fun() 定义&#xff0c;也可以直接fun() 定义,不带任何…...

STM32中独立看门狗和窗口看门狗的使用方法

独立看门狗&#xff08;Independent Watchdog&#xff0c;IWDG&#xff09;和窗口看门狗&#xff08;Window Watchdog&#xff0c;WWDG&#xff09;是STM32微控制器中提供的两种看门狗定时器。看门狗定时器是一种硬件计时器&#xff0c;用于监视系统的运行状态&#xff0c;并在…...

刷题笔记(第七天)

1.找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~) 返回数组&#xff0c;格式为 key: value结果数组不要求顺序 输入&#xff1a; var C function() {this.foo ‘bar’; this.baz ‘bim’;}; C.prototype.bop ‘bip’; iterate(new C()); 输出…...

python3.7 + pygame1.9.3实现小游戏《外星人入侵》(五):计分

本小节首先在游戏画面中添加一个Play按钮&#xff0c;用于根据需要启动游戏&#xff0c;为此在game_stats.py中输入以下代码&#xff1a; class GameStats():def __init__(self,ai_settings):# 初始化统计信息self.ai_settings ai_settingsself.reset_stats()#让游戏一开始处…...

[量化投资-学习笔记014]Python+TDengine从零开始搭建量化分析平台-Python知识点汇总

以下内容总结了之前章节涉及到的 Python 知识点&#xff0c;看过之前的章节同学&#xff0c;就不用打开了。 1. Restful 访问 TDengine 数据库 知识点&#xff1a; 发送给 TDengine 的 HTTP Body 里面是 SQL 明文&#xff0c;请求方式为 POST。TDenging 返回的结果是 JSON 格…...

[论文分享] Never Mind the Malware, Here’s the Stegomalware

Never Mind the Malware, Here’s the Stegomalware [IEEE Security & Privacy 2022] Luca Caviglione | National Research Council of Italy Wojciech Mazurczyk | Warsaw University of Technology and FernUniversitt in Hagen 近年来&#xff0c;隐写技术已逐渐被观…...

代号:408 —— 1000道精心打磨的计算机考研题

文章目录 &#x1f4cb;前言&#x1f3af;计算机科学与技术专业介绍&#xff08;14年发布&#xff09;&#x1f9e9;培养目标&#x1f9e9;毕业生应具备的知识和能力&#x1f9e9;主要课程 &#x1f3af;代号&#xff1a;408&#x1f525;文末送书&#x1f9e9;有什么优势&…...

《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互

QT版本&#xff1a;5.15.2 VS版本&#xff1a;2019 客户端程序主要包含三块&#xff1a;连接服务器&#xff0c;发送消息&#xff0c;关闭客户端 服务端程序主要包含三块&#xff1a;打开消息监听&#xff0c;接收消息并反馈&#xff0c;关闭服务端 1、先打开服务端监听功能 …...

行业追踪,2023-11-13

自动复盘 2023-11-13 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…...

开放领域对话系统架构

开放领域对话系统是指针对非特定领域或行业的对话系统&#xff0c;它可以与用户进行自由的对话&#xff0c;不受特定领域或行业的知识和规则的限制。开放领域对话系统需要具备更广泛的语言理解和生成能力&#xff0c;以便与用户进行自然、流畅的对话。 与垂直领域对话系统相比…...

终端神器:tmux

安装tmux简单使用自己的理解&#xff08;小白专属&#xff09; 使用的初衷&#xff1a; 在Linux终端下&#xff0c;由于session&#xff08;会话&#xff09;和windows&#xff08;窗口&#xff09;是绑定一起的&#xff0c;你打开一个终端的黑窗口就是打开一个会话&#xff0c…...

Elasticsearch学习(一)

ElasticSearch学习&#xff08;一&#xff09; 1 什么是Elasticsearch 1.什么是搜索&#xff1f; 百度&#xff1a;我们比如说想找寻任何信息时候就会上百度上搜索一下 比如说&#xff1a;电影、图片、小说等等…&#xff08;提到搜索的第一印象&#xff09; 百度 &#x…...

CSS3的常见边框汇总

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>CSS3 边框</title><style>body, ul, li, dl, dt, dd, h1, h2, h3, h4, h5 {margin: 0;padding: 0;}body {background-color: #F7F7F7;}.wr…...

酷柚易汛ERP-购货订单操作指南

1、应用场景 先下购货订单&#xff0c;收货入库后生成购货单。 2、主要操作 2.1 新增购货订单 打开【购货】-【购货订单】新增购货订单。&#xff08;*为必填项&#xff0c;其他为选填&#xff09; ① 录入供应商&#xff1a;点击供应商字段框的 &#xff0c;在弹框中选择供…...

【数据仓库】数仓分层方法详解与层次调用规范

文章目录 一. 数仓分层的意义1. 清晰数据结构。2. 减少重复开发3. 方便数据血缘追踪4. 把复杂问题简单化5. 屏蔽原始数据的异常6. 数据仓库的可维护性 二. 如何进行数仓分层&#xff1f;1. ODS层2. DW层2.1. DW层分类2.2. DWD层2.3. DWS 3. ADS层 4、层次调用规范 一. 数仓分层…...

记一次线上问题引发的对 Mysql 锁机制分析

背景 最近双十一开门红期间组内出现了一次因 Mysql 死锁导致的线上问题&#xff0c;当时从监控可以看到数据库活跃连接数飙升&#xff0c;导致应用层数据库连接池被打满&#xff0c;后续所有请求都因获取不到连接而失败 整体业务代码精简逻辑如下&#xff1a; Transaction p…...

Android 工厂模式距离传感器逻辑优化

Android 工厂模式距离传感器逻辑优化 接到客户反馈提到距离传感器校准完毕之后&#xff0c;每次测试完成界面都会弹出“请点击校准按钮进行校准&#xff01;”Toast弹窗&#xff0c;需要对弹窗的显示逻辑进行优化&#xff0c;即只让其在首次进入距离传感器测试界面时弹出&#…...

Dell笔记本电脑 启动时提示解决

https://www.dell.com/support/kbdoc/en-us/000139731/what-the-headless-operation-mode-active-post-message-means-and-how-to-stop-it-appearing-during-start-up dell官方解释&#xff1a; 提示来自于BIOS/UEFI固件中POST Behaviar&#xff0c;只要打开了忽略警告、错误…...

【人工智能Ⅰ】7-KNN 决策树

【人工智能Ⅰ】7-KNN & 决策树 7-1 KNN&#xff08;K near neighbour&#xff09; 思想&#xff1a;一个样本与数据集中的k个样本最相似&#xff0c;若这k个样本大多数属于某类别&#xff0c;则该个样本也属于这类别 距离度量 样本相似性用欧氏距离定义 L p ( x i , x…...

【LeetCode】26. 删除有序数组中的重复项

26. 删除有序数组中的重复项 难度&#xff1a;简单 题目 给你一个 非严格递增排列 的数组 nums &#xff0c;请你原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素…...

K8S知识点(八)

&#xff08;1&#xff09;实战入门-Label 通过标签实现Pod的区分&#xff0c;说白了就是一种标签选择机制 可以使用命令是否加了标签&#xff1a; 打标签&#xff1a; 更新标签&#xff1a; 筛选标签&#xff1a; 修改配置文件&#xff0c;重新创建一个pod 筛选&#xff1…...

25.4 MySQL 函数

1. 函数的介绍 1.1 函数简介 在编程中, 函数是一种组织代码的方式, 用于执行特定任务. 它是一段可以被重复使用的代码块, 通常接受一些输入(参数)然后返回一个输出. 函数可以帮助开发者将大型程序分解为更小的, 更易于管理的部分, 提高代码的可读性和可维护性.函数在编程语言…...

Unity 下载Zip压缩文件并且解压缩

1、Unity下载Zip压缩文件主要使用UnityWebRequest类。 可以参考以下方法&#xff1a; webRequest UnityWebRequest.Get(Path1); //压缩文件路径webRequest.timeout 60;webRequest.downloadHandler new DownloadHandlerBuffer();long fileSize GetLocalFileSize(Path2); …...

c++11新特性篇-委托构造函数和继承构造函数

C11引入了委托构造函数&#xff08;Delegating Constructor&#xff09;和C11及后续标准引入了继承构造函数&#xff08;Inheriting Constructor&#xff09;两个特性。 1.委托构造函数 委托构造函数是C11引入的一个特性&#xff0c;它允许一个构造函数调用同一类的另一个构造…...

Flink SQL处理回撤流(Retract Stream)案例

Flink SQL支持处理回撤流&#xff08;Retract Stream&#xff09;&#xff0c;下面是一个使用Flink SQL消费回撤流的案例&#xff1a; 假设有一个数据流&#xff0c;包含用户的姓名和年龄&#xff0c;希望计算每个姓名的年龄总和。 以下是示例代码&#xff1a; // 创建流执行…...

6.5.事件图层(MapEventsOverlay)

愿你出走半生,归来仍是少年&#xff01; 简单来说就是一个不参与绘制但是可进行交互的图层&#xff0c;它具备了单击和长按的交互功能。 booleanonSingleTapConfirmed(MotionEvent e, MapView mapView)booleanonLongPress(MotionEvent e, MapView mapView) 通过继承它重写上方…...

供暖系统如何实现数据远程采集?贝锐蒲公英高效实现智慧运维

山西某企业专注于暖通领域&#xff0c;坚持为城市集中供热行业和楼宇中央空调行业提供全面、专业的“智慧冷暖”解决方案。基于我国供热行业的管理现状&#xff0c;企业成功研发并推出了可将能源供应、管理与信息化、自动化相融合的ICS-DH供热节能管理系统。 但是&#xff0c;由…...