网站建设项目目标描述,怎么做招投标网站,深圳知名网站建设价格,游戏网站开发什么意思基于 Apache POI 的体检报告 Word 生成实战文档一 项目目标与总体设计
目标#xff1a;基于模板快速生成排版规范的体检报告#xff0c;支持文本替换、动态表格、图片插入#xff0c;并可一键导出 PDF 用于归档与打印。技术选型#xff1a;
Apache POI XWPF#xff1a;操作…基于 Apache POI 的体检报告 Word 生成实战文档一 项目目标与总体设计目标基于模板快速生成排版规范的体检报告支持文本替换、动态表格、图片插入并可一键导出PDF用于归档与打印。技术选型Apache POI XWPF操作.docx模板完成占位符替换、表格行循环、图片插入等。LibreOffice headless将.docx转换为.pdf跨平台、稳定可靠。工程结构建议模板resources/templates/health_report_template.docx领域模型体检人信息、体检项目结果、影像图片等服务模板解析、数据填充、PDF 转换、HTTP 下载关键约束模板必须使用.docxXWPF 不支持直接操作.doc占位符需保证在同一XWPFRun内避免替换失败。二 快速开始与最小可用示例Maven 依赖建议版本 ≥5.2.3dependencygroupIdorg.apache.poi/groupIdorg.apache.poi/groupIdartifactIdpoi-ooxml/artifactIdversion5.2.3/version/dependency最小可用流程读取模板 → 替换占位符 → 写出文件// 1) 读取模板try(InputStreamisnewClassPathResource(templates/health_report_template.docx).getInputStream();XWPFDocumentdocnewXWPFDocument(is)){// 2) 简单文本替换占位符格式${key}MapString,StringparamsMap.of(name,张三,gender,男,age,28,examDate,2025-12-01);replaceTextInDoc(doc,params);// 3) 写出 .docxtry(FileOutputStreamoutnewFileOutputStream(target/体检报告_张三.docx)){doc.write(out);}}占位符替换工具方法核心要点逐段遍历逐 Run 替换避免跨 Run 失效publicstaticvoidreplaceTextInDoc(XWPFDocumentdoc,MapString,Stringparams){for(XWPFParagraphp:doc.getParagraphs()){ListXWPFRunrunsp.getRuns();if(runs.isEmpty())continue;Stringtextp.getText();if(textnull||!text.contains(${))continue;// 简单策略将整段文本一次性替换要求占位符不被 Run 拆分for(Map.EntryString,Stringe:params.entrySet()){Stringph${e.getKey()};if(text.contains(ph)){texttext.replace(ph,e.getValue()null?:e.getValue());}}// 写回第一个 Run清空其余 Run避免残留格式runs.get(0).setText(text,0);for(intiruns.size()-1;i0;i--){p.removeRun(i);}}}运行后将在 target 目录生成体检报告_张三.docx。三 核心能力实现动态表格体检项目明细模板中预留一个表格约定第一行是表头第二行是“数据模板行”POI 会复用该行样式创建新行。// 体检项目明细staticclassItem{Stringproject;Stringresult;Stringunit;StringrefLow;StringrefHigh;Stringconclusion;}publicstaticvoidfillTable(XWPFDocumentdoc,ListItemitems){ListXWPFTabletablesdoc.getTables();if(tables.isEmpty())return;XWPFTabletabletables.get(0);// 取第一个表格// 约定第2行为模板行索引1从它之后插入数据行XWPFTableRowtpltable.getRow(1);for(inti0;iitems.size();i){XWPFTableRowrowtable.insertNewTableRowAfter(tpl);// 复制模板行的单元格样式浅拷贝POI 默认行为for(intc0;ctpl.getTableCells().size();c){XWPFTableCellsrctpl.getCell(c);XWPFTableCelldstrow.getCell(c);if(dstnull)dstrow.addNewTableCell();// 简单文本填充如需保留样式可深拷贝 CTRdst.setText(getCellText(items.get(i),c));}}// 可选移除模板行table.removeRow(1);}privatestaticStringgetCellText(Itemit,intcol){returnswitch(col){case0-it.project;case1-it.result;case2-it.unit;case3-it.refLow;case4-it.refHigh;case5-it.conclusion;default-;};}图片插入体检影像、签名等使用 POI 的 addPicture尺寸以EMU为单位1 英寸 914400EMU。publicstaticvoidinsertImage(XWPFDocumentdoc,StringimgPath,intwidthInch,intheightInch)throwsException{try(FileInputStreamisnewFileInputStream(imgPath)){// 添加图片数据并返回索引可选intidxdoc.addPictureData(is,XWPFDocument.PICTURE_TYPE_PNG);XWPFParagraphpdoc.createParagraph();XWPFRunrunp.createRun();run.addPicture(is,XWPFDocument.PICTURE_TYPE_PNG,imgPath,Units.toEMU(widthInch*914400),Units.toEMU(heightInch*914400));}}表格样式与对齐居中、宽度通过底层CTTbl/ CTTblPr/ CTJc设置表格居中与宽度单位DXA常用全宽约9000。importstaticorg.openxmlformats.schemas.wordprocessingml.x2006.main.STJc.*;publicstaticvoidsetTableStyle(XWPFTabletable){CTTblPrtblPrtable.getCTTbl().addNewTblPr();CTJcjctblPr.addNewJc();jc.setVal(STJc.CENTER);CTTblWidthwidthtblPr.addNewTblW();width.setW(BigInteger.valueOf(9000));width.setType(STTblWidth.DXA);}模板占位符被 Run 拆分的处理现象占位符被 Word 样式拆到多个XWPFRun导致简单替换失效。解决思路在 Word 中将占位符粘贴为“无格式文本”保证整体位于同一 Run或在代码中合并被拆分的 Run再替换遍历 Run定位${与}合并中间 Run 的文本后再替换。四 模板规范与最佳实践模板规范使用.docx所有占位符统一为${key}避免特殊字符与 XML 冲突。表格循环预留“表头 模板行”模板行用于复制生成数据行。图片占位预留位置段落或在代码中指定插入点。样式与编号尽量用 Word 样式标题、正文、表格样式避免手工格式影响复用。运行与资源模板放置于classpath使用ClassPathResource读取便于 JAR 包部署。所有流使用 try-with-resources 关闭避免文件句柄泄漏。性能与稳定性大数据量1万行建议分页生成多个文档或导出CSV/Excel附件。图片压缩后再写入避免体积过大。可维护性将“文本替换、表格填充、图片插入”封装为独立组件便于单元测试与复用。五 导出 PDF 与 HTTP 下载LibreOffice 转换跨平台、稳定publicstaticbooleanconvertDocxToPdf(StringinDocx,StringoutDir){StringosSystem.getProperty(os.name).toLowerCase();Stringcmd;if(os.contains(win)){cmdcmd /c start /wait soffice --headless --invisible --convert-to pdf:writer_pdf_Export inDocx --outdir outDir;}else{cmdlibreoffice --headless --invisible --convert-to pdf:writer_pdf_Export inDocx --outdir outDir;}try{ProcesspRuntime.getRuntime().exec(cmd);intexitp.waitFor();returnexit0;}catch(Exceptione){e.printStackTrace();returnfalse;}}Spring Boot 下载接口示例GetMapping(/report/export)publicvoidexport(HttpServletResponseresp)throwsException{// 1) 生成 .docxStringdocxtarget/体检报告_张三.docx;// 生成逻辑// 2) 转 PDFStringpdfdocx.replace(.docx,.pdf);convertDocxToPdf(docx,target);// 3) 输出 PDFresp.setContentType(application/pdf);resp.setHeader(Content-Disposition,attachment;filenameURLEncoder.encode(体检报告.pdf,UTF-8));Files.copy(Paths.get(pdf),resp.getOutputStream());resp.getOutputStream().flush();}提示服务器需安装LibreOfficeWindows 下建议使用安装路径下的 soffice.exe。转换是外部进程注意超时与异常捕获。六 常见问题与排查清单占位符未被替换检查占位符是否被样式拆分到多个XWPFRun统一为无格式文本或合并 Run 后再替换。生成后 Word 损坏避免并发写同一文档确保所有流关闭POI 版本升级到稳定版≥5.2.3。图片不显示或变形尺寸单位使用EMU确认图片格式与 addPicture 类型一致必要时压缩图片。表格样式丢失通过复制模板行样式或操作底层CTTc/CTP保留格式必要时设置表格居中与宽度。Linux 转 PDF 失败检查 LibreOffice 是否安装、命令路径、权限与可用字体查看进程退出码与日志。七 一键运行与扩展建议一键运行步骤准备模板resources/templates/health_report_template.docx含name、{name}、name、{gender}、age、{age}、age、{examDate} 与“项目明细”表格。运行单元测试或直接执行 main 方法生成.docx与.pdf。打开 PDF 校验排版、表格、图片与编码中文。扩展建议模板引擎化将模板标签升级为##{foreachRows}##、##{foreachTable}##等实现表格行/表格级循环与更灵活的布局。页眉页脚与页码操作XWPFHeaderFooter与底层CTP添加页码域。电子签名/二维码生成图片后插入页脚或指定区域。多格式导出同时支持.docx/.pdf大数据量导出Excel汇总。异步与缓存报告生成放入异步任务生成后缓存 PDF避免重复渲染。附 模板占位符与表格示例文本占位符示例姓名${name}性别${gender}年龄${age}体检日期${examDate}表格示例项目明细项目结果单位参考低参考高结论身高175cm体重68kg收缩压118mmHg90120正常舒张压76mmHg6080正常将以上表格的第二行作为“模板行”程序会复制该行生成数据行最终移除模板行。参考要点使用Apache POI XWPF操作.docx模板完成文本替换、表格行循环与图片插入模板占位符需位于同一XWPFRun内避免替换失败。通过LibreOffice headless将.docx转换为.pdf适合服务器批量导出与归档。图片插入需使用EMU单位设置宽高表格可通过底层CTTbl/CTJc设置居中与全宽保证打印版式稳定。