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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/143108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习 植物识别算法系统 计算机竞赛

文章目录 0 前言2 相关技术2.1 VGG-Net模型2.2 VGG-Net在植物识别的优势(1) 卷积核&#xff0c;池化核大小固定(2) 特征提取更全面(3) 网络训练误差收敛速度较快 3 VGG-Net的搭建3.1 Tornado简介(1) 优势(2) 关键代码 4 Inception V3 神经网络4.1 网络结构 5 开始训练5.1 数据集…

arf_1解题

arf_1解题 镜像环境 version: 3.2services:web:image: registry.cn-hangzhou.aliyuncs.com/n1book/web-file-read-1:latestports:- 80:80新建yml文件将代码保存在当前位置 使用docker-compost up -d 拉取镜像 解题 访问该镜像映射端口为1520 可以看到页面只有一个holle但…

【Python】一文带你掌握数据容器之集合,字典

目录&#xff1a; 一、集合 思考&#xff1a;我们目前接触到了列表、元组、字符串三个数据容器了。基本满足大多数的使用场景为何又需要学习新的集合类型呢? 通过特性来分析: &#xff08;1&#xff09;列表可修改、支持重复元素且有序 &#xff08;2&#xff09;元组、字符…

Home Assistant使用ios主题更换背景

Home Assistant使用ios主题、更换背景 lovelace-ios-dark-mode-theme 默认前置情况&#xff0c;1、已安转HACS插件2、搜索安装 IOS Dark Mode Theme1&#xff09;第一、二步应该很容易实现&#xff0c;configuration.yaml文件很容易被找到2&#xff09;而本人在进行第三步操作时…

php+vue3实现点选验证码

buildadmin 中的点选验证码实现 验证码类 <?phpnamespace ba;use Throwable; use think\facade\Db; use think\facade\Lang; use think\facade\Config;/*** 点选文字验证码类*/ class ClickCaptcha {/*** 验证码过期时间(s)* var int*/private int $expire 600;/*** 可以…

我记不住的那些命令(不断更新中)

fzf 一种进行模糊查找的命令行工具 主页&#xff1a;https://github.com/junegunn/fzf 我的主机是 Kali&#xff0c;通过apt进行安装fzf&#xff0c;并进行配置。 # apt install fzf # apt show fzf 通过参考/usr/share/doc/fzf/README.Debian来进行快速配置快捷键和自动补…

保护您的Google账号安全:检查和加固措施

简介&#xff1a;随着我们在日常生活中越来越依赖于Google账号&#xff0c;我们的个人信息和敏感数据也变得越来越容易受到威胁。为了确保您的Google账号的安全性&#xff0c;本文将介绍一些简单但有效的方法&#xff0c;帮助您检查和加固您的Google账号。 --- 在数字时代&am…

squid代理服务器

squide是正向代理&#xff0c;缓存加速。基于aci访问控制协议。可以过滤控制。可以实现访问控制 squid代理的工作机制 nginx是转发 1、 squid是代替客户端向网站请求数据&#xff0c;不需要访问代理的ip地址。直接请求目的网站即可。由代理服务器处理请求和响应。squid也可以…

[Mac软件]Adobe Media Encoder 2024 V24.0.2免激活版

软件说明 使用Media Encoder&#xff0c;您将能够处理和管理多媒体。插入、转码、创建代理版本&#xff0c;并几乎以任何可用的格式输出。在应用程序中以单一方式使用多媒体&#xff0c;包括Premiere Pro、After Effects和Audition。 紧密整合 与Adobe Premiere Pro、After …

WebRTC简介及使用

文章目录 前言一、WebRTC 简介1、webrtc 是什么2、webrtc 可以做什么3、数据传输需要些什么4、SDP 协议5、STUN6、TURN7、ICE 二、WebRTC 整体框架三、WebRTC 功能模块1、视频相关①、视频采集---video_capture②、视频编解码---video_coding③、视频加密---video_engine_encry…

保护数字前沿:下一代防火墙如何塑造网络安全的未来

下一代防火墙通过提供先进的威胁检测、精细控制和云安全功能&#xff0c;正在重塑网络安全的未来。随着数字环境的不断发展&#xff0c;组织必须采用这些创新解决方案来保护其数字资产并维护安全的数字前沿。 在当今互联的世界中&#xff0c;网络威胁变得越来越复杂&#xff0c…

阿里云ACK(Serverless)安装APISIX网关及APISIX Ingress Controller

在k8s上安装apisix全家&#xff0c;通过helm安装很简单&#xff0c;但是会遇到一些问题。 安装 首先登录阿里云控制台&#xff0c;在ACK集群详情页&#xff0c;进入CloudShell&#xff0c;执行下面helm命令安装apisix、apisix-ectd、apisix-dashboard和apisix-ingress-contro…

第1关:构造函数与析构函数的实现

题目&#xff1a;根据.h写出.cpp 考点&#xff1a; 1.链表的默认构造&#xff0c; 拷贝构造&#xff0c;传参构造以及析构函数等。 代码&#xff1a; /********** BEGIN **********/ #include <cstdlib> #include <cstring> #include "LinkedList.h&…

ARM PMU

PMU单元概览 ARM PMU概要 PMU作为一个扩展功能&#xff0c;是一种非侵入式的调试组件。 对PMU寄存器的访问可以通过CP15协处理器指令和Memory-Mapped地址。 基于PMUv2架构&#xff0c;A7处理器在运行时可以收集关于处理器和内存的各种统计信息。对于处理器来说这些统计信息中…

AI:83-基于深度学习的手势识别与实时控制

🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新中,…

【Linux奇遇记】我和Linux的初次相遇

&#x1f308;个人主页: Aileen_0v0 &#x1f525;系列专栏:Linux奇遇记系列专栏&#x1f4ab;"没有罗马,那就自己创造罗马~" 目录 前端和后端的介绍 1.前端 2.后端 3.前后端区别 Linux在前后端开发中的角色 如何学习Linux 去进行程序开发 Linux的常见根目…

爆款元服务!教你如何设计高使用率卡片

元服务的概念相信大家已经在 HDC 2023 上有了很详细的了解&#xff0c;更轻便的开发方式&#xff0c;让开发者跃跃欲试。目前也已经有很多开发者开发出了一些爆款元服务&#xff0c;那么如何让你的元服务拥有更高的传播范围、更高的用户使用率和更多的用户触点呢&#xff1f;设…

MySQL 索引事务

MySQL 索引&事务 文章目录 MySQL 索引&事务1. 索引1.1 概念1.2 作用1.3 使用场景1.4 使用 2. 事务2.1 为什么使用事务2.2 事务概念2.3 事务的特性2.4 使用 1. 索引 1.1 概念 索引(index)是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的…

学好Python-新手小白如何做?

新手小白如何学好Python?有哪些参考方法吗?这是一个老生常谈的话题了。今天为大家带来两位前辈的分享&#xff0c;他们给出了非常实用的方法和思路&#xff0c;希望对你有所帮助。 1、多练&#xff0c;两个字&#xff1a;多练 如果真的要说方法可以参考如下&#xff1a; ①…