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

若依定制pdf生成实战

一、介绍

使用 Java Apache POI 将文字渲染到 Word 模板是一种常见的文档自动化技术,广泛应用于批量生成或定制 Word 文档的场景。使用aspose可以将word转成pdf从而达到定制化pdf的目的。

参考文档:java实现Word转Pdf(Windows、Linux通用) - 故城归人 - 博客园

二、word模板准备

(1)pdf逆转

本次实践没有word模板,需从生成的pdf先还原成word。所以使用python将pdf逆转成word docx:

!pip install pdf2docxfrom pdf2docx import Converter
pdf_file = '专业技术资格评审申报表_预览.pdf'
docx_file = '申报表模板.docx'
cv = Converter(pdf_file)
#cv.convert(docx_file, start=0, end=2)  # 只转前三页
cv.convert(docx_file)# 转全部
cv.close()

创建word模板,在其中插入占位符,例如 {{name}}、{{workorg}}

(2)模版读取方式

   绝对路径读取:缺点路径变化无法读出

String baseDir = System.getProperty("user.dir");//获取项目根目录
String templatePath = baseDir + "/peer-upms/peer-upms-biz/src/main/resources/file/title_template.docx";
File templateFile = new File(templatePath);

   classpath读取:这样就不再依赖项目根目录的绝对路径,打包之后只要 title_template.docx 在 src/main/resources/file/ 下,就能自动被拷贝并使用。之后你再用 templateFile 打开 POI 进行替换就能正常运行了。

// 1. 从 classpath 加载模板资源(src/main/resources/file/title_template.docx)
ClassPathResource tplRes = new ClassPathResource("file/title_template.docx");
if (!tplRes.exists()) {throw new FileNotFoundException("classpath:/file/title_template.docx 不存在");
}// 2. 将模板复制到一个临时文件(因为 POI 需要 File)
File templateFile = File.createTempFile("title_cover_", ".docx");
try (InputStream in = tplRes.getInputStream();FileOutputStream out = new FileOutputStream(templateFile)) {in.transferTo(out);}

三、Java相关代码

word部分(渲染数据写死)

(1)引入依赖:

   <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.1.0</version><scope>compile</scope></dependency>

(2)word下载接口:

 @Operation(summary = "申请人DOCX下载",responses = {@ApiResponse(responseCode = "200",content = @Content(mediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document",schema = @Schema(type = "string", format = "binary")))})@RequestMapping(value = "/download/docx", method = RequestMethod.GET)public ResponseEntity<Resource> downloadDocx() {Map<String, Object> data = new HashMap<>();data.put("name", "XX1");data.put("department", "XX111测试");data.put("zhuanye", "作物育种");data.put("jishuzige", "研究员");data.put("nian", "2025");data.put("yue", "03");data.put("ri", "20");data.put("datetime", "2025/03/20 14:11:44");data.put("zhuanyejishu", "生命科学研究");try {File docxFile = wordToPdfService.generateDocx(data);File file = new File(docxFile.getName());if (!file.exists()) {return ResponseEntity.notFound().build();}Resource resource = new FileSystemResource(file);if (!resource.exists() || !resource.isReadable()) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"));String fileName = "专业技术资格申报表_" + data.get("name") + "_预览.docx";
//            headers.setContentDisposition(ContentDisposition.builder("attachment").filename(fileName).build());//解决中文乱码问题ContentDisposition contentDisposition = ContentDisposition.builder("attachment").filename(fileName, StandardCharsets.UTF_8).build();headers.setContentDisposition(contentDisposition);headers.setContentLength(file.length());return new ResponseEntity<>(resource, headers, HttpStatus.OK);} catch (Exception e) {e.printStackTrace();return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}}
@Service
public class WordToPdfService {/**** 根据word模板生成word文档**/public File generateDocx(Map<String, Object> data) throws Exception {String baseDir = System.getProperty("user.dir");String templatePath = baseDir + "/peer-upms/peer-upms-biz/src/main/resources/file/title_template.docx";File templateFile = new File(templatePath);if (!templateFile.exists()) {throw new Exception("Template file does not exist at: " + templatePath);}try (FileInputStream fis = new FileInputStream(templateFile)) {if (fis.available() <= 0) {throw new Exception("Template file is empty.");}}XWPFDocument document = WordExportUtil.exportWord07(templatePath, data);try (FileOutputStream fos = new FileOutputStream("output.docx")) {document.write(fos);}File docxFile = new File("output.docx");return docxFile;}
......

pdf部分(渲染数据从mysql中拉取)

(1) 引入jar包:

mvn install:install-file   -Dfile=peer-upms/peer-upms-biz/libs/aspose-words-15.8.0-jdk16.jar   -DgroupId=com.aspose   -DartifactId=aspose-words   -Dversion=15.8.0-jdk16   -Dpackaging=jar

(2)下载pdf接口(先转word再生成对应的pdf)


@Inner(value = false)
@RestController
@RequiredArgsConstructor
public class TitlePdfDownloadController {@Autowiredprivate ApplyDataPdfPreviewService applyDataPdfPreviewService;@GetMapping(value = "/downloadPdf")@Inner(value = false)public ResponseEntity<Resource> downloadWord() {try {Map<String, Object> data = new HashMap<>();data = applyDataPdfPreviewService.getDataByApplyId(applyId);// 1. Generate DOCX fileFile docxFile = wordToPdfService.generateDocx(data);// 2. Convert DOCX to PDFFile pdfFile = wordToPdfService.convertDocxToPdf(docxFile);// 3. Read PDF file bytesbyte[] pdfBytes = Files.readAllBytes(pdfFile.toPath());// 4. Set file name and response headers with PDF MIME typeString fileName = "专业技术资格申报表_" + data.get("name") + "_" + "预览.pdf";Resource resource = new ByteArrayResource(pdfBytes);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_PDF);headers.setContentDispositionFormData("attachment", fileName);headers.setContentLength(pdfBytes.length);return new ResponseEntity<>(resource, headers, HttpStatus.OK);} catch (Exception e) {e.printStackTrace();return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}}

(3)将动态从mysql获取数据写成一个service:


@Service
public class ApplyDataPdfPreviewService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate FindCitiidMapper findCitiidMapper;/*** 根据 applyId 拉取申报表、学历和奖惩信息,* 并组装到一个 Map 中返回。*/public Map<String,Object> getDataByApplyId(long applyId) {Map<String,Object> data = new HashMap<>();// 0)拉取 title_apply_applypers 中的mobile字段String citiid = findCitiidMapper.findCitiidByApplyId(applyId);String sqlMobile = "select mobile from title_apply_applypers where citiid=?";Map<String,Object> mobile = jdbcTemplate.queryForMap(sqlMobile, citiid);data.put("mobile", mobile.get("mobile"));// 1) 拉取 title_applytable 中的所有字段String sqlMain = """SELECTexctypecd,techqualtype_cd,citiid,name,workorg,gend_cd,birthday,job_dt,presdivtechjob,discactisitu,presdivtechpost,presdivtechpostbgn_tm,apply_id,apply_serie_nm,applydiv_nm,prestechqual_nm,fillin_tm,perm_nm,permget_tm,permcertnum,divtype,prestechqualget_tm,prestechqualapprorg_nm,presdivtechjobbyear,applytechqualdetail_cd,innovationteamFROM title_applytableWHERE apply_id = ?""";Map<String,Object> main = jdbcTemplate.queryForMap(sqlMain, applyId);// 基本字段放入 mapString exctypecd = main.get("exctypecd").toString();switch (exctypecd) {case "1":data.put("c1",                 "√");data.put("c2",                 " ");data.put("c3",                 " ");data.put("c4",                 " ");break;case "2":data.put("c1",                 " ");data.put("c2",                 "√");data.put("c3",                 " ");data.put("c4",                 " ");break;case "5":data.put("c1",                 " ");data.put("c2",                 " ");data.put("c3",                 "√");data.put("c4",                 " ");break;case "6":data.put("c1",                 " ");data.put("c2",                 " ");data.put("c3",                 " ");data.put("c4",                 "√");break;default:data.put("c1",                 " ");data.put("c2",                 " ");data.put("c3",                 " ");data.put("c4",                 " ");break;}String techqualtype_cd = main.get("techqualtype_cd").toString();String techType ="";switch (techqualtype_cd) {case "1":techType = "应用基础研究与技术开发人员";break;case "2":techType = "试验发展与转化应用人员";break;case "3":techType = "农业政策与科技管理研究人员";break;default:techType = "";break;}data.put("techType",                techType);......// 2) 拉取学历信息列表String sqlEdu = """SELECTschool_nm,edu_nm,edutype_cd,edu_cd,digree_cd,gradsitu_cd,edubegin_tm,grad_tmFROM title_edurecWHERE apply_id = ?ORDER BY edubegin_tm""";List<Map<String,Object>> edus = jdbcTemplate.queryForList(sqlEdu, applyId);for (int i = 0; i < 4; i++) {Map<String,Object> e = i < edus.size() ? edus.get(i) : new HashMap<>();int idx = i + 1;data.put("school_nm" + idx,    e.get("school_nm") != null ? e.get("school_nm") : " ");data.put("edu_nm" + idx,       e.get("edu_nm") != null ? e.get("edu_nm") : " ");data.put("edutype_cd" + idx,   e.get("edutype_cd") != null ? e.get("edutype_cd") : " ");data.put("edu_cd" + idx,       e.get("edu_cd") != null ? e.get("edu_cd") : " ");data.put("digree_cd" + idx,    e.get("digree_cd") != null ? e.get("digree_cd") : " ");data.put("gradsitu_cd" + idx,  e.get("gradsitu_cd") != null ? e.get("gradsitu_cd") : " ");data.put("edubegin_tm" + idx,  e.get("edubegin_tm") != null ? e.get("edubegin_tm") : " ");data.put("grad_tm" + idx,      e.get("grad_tm") != null ? e.get("grad_tm") : " ");}// 3) 拉取奖惩信息列表String sqlBonus = """SELECTbonus_nm,bonus_tm,bonusorgFROM title_bonusWHERE apply_id = ?ORDER BY bonus_tm DESC""";List<Map<String,Object>> bonuses = jdbcTemplate.queryForList(sqlBonus, applyId);for (int i = 0; i < 3; i++) {Map<String,Object> b = i < bonuses.size()  ? bonuses.get(i) : new HashMap<>();int idx = i + 1;data.put("bonus_nm" + idx, b.size() != 0 ? b.get("bonus_nm") : " ");data.put("bonus_tm" + idx, b.size() != 0  ? b.get("bonus_tm") : " ");data.put("bonusorg" + idx, b.size() != 0 ? b.get("bonusorg") : " ");}data.put("nian", java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy")));data.put("yue", java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("MM")));data.put("ri", java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("dd")));data.put("print_time", java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));// 4) 继续教育情况String sqlContEdu = """SELECT begin_tm, end_tm, content, chargorg, studyaddrFROM title_applycontedusituWHERE apply_id = ?ORDER BY begin_tm""";List<Map<String,Object>> contEdus = jdbcTemplate.queryForList(sqlContEdu, applyId);for (int i = 0; i < 4; i++) {Map<String,Object> ce = i < contEdus.size() ? contEdus.get(i) : new HashMap<>();int idx = i + 1;data.put("cont_b" + idx, ce.size() != 0 ? ce.get("begin_tm") : " ");data.put("cont_e" + idx, ce.size() != 0 ? ce.get("end_tm") : " ");data.put("content" + idx, ce.size() != 0 ? ce.get("content") : " ");data.put("contedu_char" + idx, ce.size() != 0 ? ce.get("chargorg") : " ");data.put("caddr" + idx, ce.size() != 0 ? ce.get("studyaddr") : " ");}// 5) 专业考试成绩情况String sqlExam = """SELECT exam_tm, org, project, score, qualifycodeFROM title_professionalexaminfoWHERE apply_id = ?ORDER BY exam_tm""";List<Map<String,Object>> exams = jdbcTemplate.queryForList(sqlExam, applyId);for (int i = 0; i < 4; i++) {Map<String,Object> ex = i < exams.size() ? exams.get(i) : new HashMap<>();int idx = i + 1;data.put("exam_tm" + idx, ex.size() != 0 ? ex.get("exam_tm") : " ");data.put("exam_org" + idx, ex.size() != 0 ? ex.get("org") : " ");data.put("exam_project" + idx, ex.size() != 0 ? ex.get("project") : " ");data.put("escore" + idx, ex.size() != 0 ? ex.get("score") : " ");data.put("exam_qualifycode" + idx, ex.size() != 0 ? ex.get("qualifycode") : " ");}// 6) 工作经历String sqlWork = """SELECT begin_tm, end_tm, workposi, workcont, postFROM title_workresuWHERE apply_id = ?ORDER BY begin_tm""";List<Map<String,Object>> works = jdbcTemplate.queryForList(sqlWork, applyId);for (int i = 0; i < 4; i++) {Map<String, Object> w = i < works.size() ? works.get(i) : new HashMap<>();int idx = i + 1;data.put("workresu_begin" + idx, w.size() != 0 ? w.get("begin_tm") : " ");data.put("workresu_end" + idx, w.size() != 0 ? w.get("end_tm") : " ");data.put("workresu_workposi" + idx, w.size() != 0 ? w.get("workposi") : " ");data.put("workresu_workcont" + idx, w.size() != 0 ? w.get("workcont") : " ");data.put("workresu_post" + idx, w.size() != 0 ? w.get("post") : " ");}// 7) 著作、论文及报告登记String sqlArticle = """SELECT artititle, publorg, charorg, volumes, publish_time, persdutyFROM title_applyarticleWHERE apply_id = ?ORDER BY publish_time""";List<Map<String,Object>> articles = jdbcTemplate.queryForList(sqlArticle, applyId);for (int i = 0; i < 4; i++) {Map<String, Object> w = i < articles.size() ? articles.get(i) : new HashMap<>();int idx = i + 1;data.put("article_artititle" + idx, !w.isEmpty() ? w.get("artititle") : " ");data.put("article_publorg" + idx, !w.isEmpty() ? w.get("publorg") : " ");data.put("article_charorg" + idx, !w.isEmpty() ? w.get("charorg") : " ");data.put("article_volumes" + idx, !w.isEmpty() ? w.get("volumes") : " ");data.put("article_publish_time" + idx, !w.isEmpty() ? w.get("publish_time") : " ");data.put("article_persduty" + idx, !w.isEmpty() ? w.get("persduty") : " ");}// 8) 任现职以来考核情况String sqlAssess = """SELECT year, divtechpost, asserslt, asseorg, remarkFROM title_applyassesituWHERE apply_id = ?ORDER BY year""";List<Map<String,Object>> assesses = jdbcTemplate.queryForList(sqlAssess, applyId);for (int i = 0; i < 4; i++) {Map<String, Object> as = i < assesses.size() ? assesses.get(i) : new HashMap<>();int idx = i + 1;data.put("assess_year"        + idx, !as.isEmpty() ? as.get("year") : " ");data.put("assess_divtechpost" + idx, !as.isEmpty() ? as.get("divtechpost") : " ");data.put("assess_asserslt"    + idx, !as.isEmpty() ? as.get("asserslt") : " ");data.put("assess_asseorg"     + idx, !as.isEmpty() ? as.get("asseorg") : " ");data.put("assess_remark"      + idx, !as.isEmpty() ? as.get("remark") : " ");}// 9) 工作总结(单值)String sqlSumm = """SELECT presjobsummFROM title_applytableWHERE apply_id = ?""";String summary = jdbcTemplate.queryForObject(sqlSumm, String.class, applyId);data.put("presjobsumm", summary);return data;}

(4)使用aspose将word转pdf

 /*** 商业版pdf许可 license,从 classpath 下读取 license.xml*/public static boolean getLicense() {boolean result = false;InputStream is = null;try {Resource resource = new ClassPathResource("license.xml");is = resource.getInputStream();License aposeLic = new License();aposeLic.setLicense(is);result = true;} catch (Exception e) {e.printStackTrace();}finally {if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}}return result;}/*** 将传入的 DOCX 文件转换成带完整样式的 PDF 并返回该 File。** @param docxFile  Word 模板(已填充占位符)的 File* @return           生成的 PDF 临时文件* @throws Exception 转换失败时抛出*/public File convertDocxToPdf(File docxFile, long applyId) throws Exception {getLicense(); // 验证License 若不验证则转化出的pdf文档会有水印产生// 1. 构造输出 PDF 的临时文件String uuid = IdUtil.simpleUUID();String fileName = uuid+".pdf";Path targetPath = Paths.get(fileLocalPath, "PreviewPdf", fileName);File pdfFile = new File(targetPath.toString());pdfFile.getParentFile().mkdirs();TitleApplyApplydataappe applyDataAppe = new TitleApplyApplydataappe();applyDataAppe.setApplyId(applyId);applyDataAppe.setAppefile(fileName);applyDataAppe.setFilesize(pdfFile.length());applyDataAppe.setAppefiledispnm("专业技术资格申报表预览_"+applyId+".pdf");applyDataAppe.setOpTm(LocalDateTime.now());applyDataAppe.setDir("PreviewPdf");titleApplyApplydataappeService.save(applyDataAppe);// 使用商业破解版 Aspose.Words 完成转换try (FileOutputStream os = new FileOutputStream(pdfFile)) {// Document 可以直接接收 File 或 String 路径Document doc = new Document(docxFile.getAbsolutePath());doc.save(os, SaveFormat.PDF);}return pdfFile;}

四、部署

单体后端jar包部署

docker run -d --name peer-boot   -v $PWD/peer-boot.jar:/app/peer-boot.jar -v /usr/share/fonts:/usr/share/fonts   --net host   eclipse-temurin:17-jdk   java -Dfile.encoding=utf-8 -jar /app/peer-boot.jar

http://www.xdnf.cn/news/354637.html

相关文章:

  • neo4j图数据库基本概念和向量使用
  • AI云防护真的可以防攻击?你的服务器用群联AI云防护吗?
  • ESD防护ANT静电防护方案
  • 学前数学思维:初始行程
  • Docker常见疑难杂症解决指南:深入解析与实战解决方案
  • Spring 框架实战:如何实现高效的依赖注入,优化项目结构?
  • UE5骨骼插槽蓝图
  • 了解Hadoop
  • 互联网大厂Java求职面试:基于AI的实时异常检测系统设计与实现
  • PCB设计时如何选择USART、SPI、I2C
  • 【图像大模型】Stable Diffusion Web UI:深度解析与实战指南
  • 单调栈模版型题目(3)
  • 第20篇:Linux设备驱动程序入门<七>
  • 8b10b编解码仿真
  • 前端自学入门:HTML 基础详解与学习路线指引
  • WebRTC 源码原生端Demo入门-1
  • 【大模型ChatGPT+ArcGIS】数据处理、空间分析、可视化及多案例综合应用
  • 鸿蒙电脑:五年铸剑开新篇,国产操作系统新引擎
  • 机器人运动控制技术简介
  • SpringAI特性
  • Vscode 顶部Menu(菜单)栏消失如何恢复
  • 操作系统面试题(3)
  • C++之运算符重载实例(日期类实现)
  • 云上系统CC攻击如何进行检测与防御?
  • 【Rust测试】Rust代码测试方法详解与应用实战
  • Mac配置php开发环境(多PHP版本,安装Redis)
  • JDK8 HashMap红黑树退化为链表的机制解析
  • 第五节:对象与原型链:JavaScript 的“类”与“继承”
  • odoo-049 Pycharm 中 git stash 后有pyc 文件,如何删除pyc文件
  • RslRlOnPolicyRunnerCfg 学习