尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Java docx文本替换与PDF转换实战:POI操作和无头office避坑指南
简介面向需要在Java项目中批量处理Word文档的开发者这份资源提供了基于Apache POI对docx文件进行文本替换再借助iText结合docx4j转换为PDF的完整工程实现。资源定位清晰覆盖从引入Maven依赖、遍历段落与Run对象替换文本到另存为输出docx、再经HTML中间格式转成PDF的核心链路适合有Java基础、正在处理文档自动化任务的工程师参考。压缩包共54个文件以44个jar依赖库为主含poi-ooxml、docx4j、batik系列、fop等另有Java源文件、编译后的class、用于验证的docx样例以及Eclipse工程配置文件整体大小27.98MB解压后可导入IDE直接查看和运行。已有1195人学习下载说明该方案对相似需求具有较高参考价值。资源内含可直接运行的Java源码2个.java、测试用docx文档和配套第三方库免去自行逐一下载jar的麻烦同时保留了工程目录结构便于对照学习Apache POI文本替换与iText转PDF的完整流程可在此基础上扩展为批量处理工具解决签名、归档等场景下的文档格式转换需求。1. Java 开发处理 docx 文件文本替换后转 PDF为什么总在交付前翻车Java 开发处理 docx 文件最常见的一句话需求是把模板合同里的 {乙方姓名}、{合同金额}、{签订日期} 替换成真实数据再把整个 docx 转 PDF 对外交付。听起来不过是一个字符串 replace 加一个格式转换实操里翻车点却一个接一个模板文本被 POI 拆成多个 run、PDF 里中文变成方块、页眉页脚的占位符一动不动、替换后表格把整页撑变形。这套方案以 POI 做 docx 文件文本替换再通过无头模式的办公套件完成 docx 转 PDF把最容易崩的 run 合并、多容器遍历、字体映射和并发隔离单独拿出来讲清楚。做完你能得到一份能直接跑起来的替换转 PDF 工具也知道每步的失败现场长什么样值不值得直接用在生产环境。2. 文本替换先过 run 这一关从整段替换到多容器遍历2.1 POI 文本模型最小单元为什么 getText() 之后不能无脑 replacePOI 对 .docx 的抽象里有三个关键层级XWPFDocument 对应文档XWPFParagraph 对应段落XWPFRun 对应段落里的文本块。Word 在保存文本时不是按自然语言逻辑连续存的而是碰到底纹、字体、字号、加粗、修订标记都会切出一个新的 run。比如模板里写了一句甲方为 {乙方姓名}看起来是一个词实际在 XML 里可能被切成了三个 run一个存甲方为 一个存{乙方一个存姓名}。你打开 document.getParagraphs()对每个段落直接 paragraph.getText().replace() 替换replace 出来确实是对的但写回段落时没有任何 API 让你把整段文字一次性塞回去只能通过 run 级别的 setText 操作这时候占位符到底落在哪个 run 里就成了玄学。很多人第一版会写出这样的代码拿到段落 getText()替换完 newText然后遍历运行列表把 newText 写进第一个 run其余 run 清空。这个做法速度最快但代价是整段样式坍缩因为第一个 run 的字体格式会成为全段的显示样式原本模板里金额两个字特意用的红色加粗会全部丢失。如果模板段落从头到尾样式统一这个方案完全够用也是我平时最常用的做法如果段落内有混排就得先定位包含占位符的 run只替换那一个 run 的文本占位符被拆开时再把拆分部分合并到首个命中 run 里。// 段落级字面替换适合模板段落内样式统一的场景 static void replaceParagraph(XWPFParagraph paragraph, MapString, String mappings) { String origin paragraph.getText(); if (origin null || origin.isEmpty()) { return; } String target origin; for (Map.EntryString, String entry : mappings.entrySet()) { // 用字面替换而不是 replaceAll避免模板/数据里的 $、[] 等符号被当成正则 target target.replace(entry.getKey(), entry.getValue()); } if (target.equals(origin)) { return; } ListXWPFRun runs paragraph.getRuns(); if (runs.isEmpty()) { return; } runs.get(0).setText(target, 0); for (int i 1; i runs.size(); i) { runs.get(i).setText(, 0); } }这段代码的关键点有三个。第一getText() 返回的是该段落所有 run 文本的拼接不是 XML 原始文本所以拿到的东西是连续可读的第二setText(value, 0) 里的 0 表示把新文本写到该 run 的 0 号文本节点并覆盖原有内容并不是追加第三占位符统一用 {} 或 ${} 并且在替换 map 里避免出现 null value 时String.replace 的字面替换语义最省心。如果你的业务数据里包含换行setText 一个带 \n 的字符串并不会产生真正的换行它只会把换行动作吞掉稳妥做法是先按 \n 拆段每段单独生成 run再在 run 之间插入 CT_Br 对象。这个细节可以等遇到真实需求再处理不用提前为它设计复杂代码。刚才那段代码能解决 80% 的问题剩下 20% 在模板样式严格要求时暴露一段里两种字体替换后统一成了一种。所以我在生产代码里通常再加一个判断只有段落的 run 数量大于 1 并且原文确实匹配占位符时才使用合并到第一个 run的策略否则就直接对命中的 run 做单 run 替换。这样至少不会把模板原有的加粗、下划线全冲掉。2.2 正文、表格、页眉页脚都要替换一个遍历函数覆盖全部节点文本替换第二个坑是遍历范围。初学者的第一个版本往往只写 for (XWPFParagraph p : document.getParagraphs())一排脑袋想当然以为页面上的每一行文字都属于文档正文段落。实际上页眉、页脚、表格单元格里的段落都是独立容器POI 的 getParagraphs() 只返回 body 元素里直接挂在文档 body 下的段落。页眉里的公司名、页脚里的页码说明、表格里的条款这些地方如果有占位符单独遍历正文根本碰不到。我见过一个线上事故模板页眉里有 {合同编号}业务方反馈替换后 PDF 上编号是空的原因就是替换方法压根没走进 header。正确做法是分层遍历把正文段落、表格单元格段落、页眉段落、页脚段落全部收集起来统一处理。表格还要注意嵌套情况一个单元格里再嵌一个表格是 Word 允许的结构只处理一层 row.getTableCells() 不够需要递归。public static void replaceAllInDocx(XWPFDocument doc, MapString, String mappings) { ListXWPFParagraph targetParagraphs new ArrayList(); for (IBodyElement element : doc.getBodyElements()) { if (element instanceof XWPFParagraph paragraph) { targetParagraphs.add(paragraph); } else if (element instanceof XWPFTable table) { collectCellParagraphs(table, targetParagraphs); } } for (XWPFHeader header : doc.getHeaderList()) { targetParagraphs.addAll(header.getParagraphs()); } for (XWPFFooter footer : doc.getFooterList()) { targetParagraphs.addAll(footer.getParagraphs()); } for (XWPFParagraph paragraph : targetParagraphs) { replaceParagraph(paragraph, mappings); } } private static void collectCellParagraphs(XWPFTable table, ListXWPFParagraph out) { for (XWPFTableRow row : table.getRows()) { for (XWPFTableCell cell : row.getTableCells()) { out.addAll(cell.getParagraphs()); // 嵌套表格cell 里可能还有一张表 for (XWPFTable nested : cell.getTables()) { collectCellParagraphs(nested, out); } } } }这里有一个容易忽略的点getHeaderList() 和 getFooterList() 拿到的是 XWPFHeaderFooter 对象它内部还有自己的 table如果页眉或页脚里不巧也放了表格上面这段代码的 header.getParagraphs() 会漏掉位于页眉表格中的文本。我在实际项目里是再写一个重载方法把 XWPFHeaderFooter 里的 table 也走一遍 collectCellParagraphs逻辑跟 body 完全一样。页眉表格虽然少见但一旦出现就是最隐蔽的替换不生效排查成本极高宁可一开始把遍历写完整。还有一个细节是表格的单元格里是否会有多个段落。cell.getParagraphs() 返回的是单元格内所有段落而不是只有第一段所以不要用 getParagraphArray(0) 这种取第一段的方式否则多行单元格里第二行之后的占位符会直接被漏掉。实现工具方法时统一用 getParagraphs()再逐个处理是最稳妥的习惯。3. docx 转 PDFPOI 止步于版式引擎选无头 office 还是 docx4j3.1 POI 为什么不提供 writeAsPdf替换后的 PDF 由谁渲染POI 的 XWPFDocument 能读写 docx但从来没有原生方法直接把 docx 输出成 PDF。原因不复杂PDF 不是简单把文本拼出来它需要对每个段落做换行计算、对每个单元格做列宽分配、对字体做嵌入和子集化还要处理文本框、图片、页码域这些对象。docx 本身只是描述结构排版结果是 Word 根据页面尺寸和字体信息现算出来的。POI 不包含那套排版引擎所以拿到替换后的 XWPFDocument 对象你只能先 write 成新的 docx 文件再由外部程序完成渲染。生产环境里常见路线有两条。第一条是用同族的无头办公套件soffice --headless --convert-to pdf它启动一个不带界面的文档进程按 Word 兼容算法重新排版并输出 PDF。保真度靠前字体问题可控缺点是依赖外部二进制服务器上必须装好对应套件而且并发调用时要处理进程和用户配置目录。第二条是纯 Java 方案用 docx4j 的 PDF 转换模块它内部通过 FOP 把 docx 的 WordprocessingML 映射成 XSL-FO 再渲染。好处是少一个外部进程但依赖体积大很多版本的 FOP 对表格边框、页脚标题、中文文本域渲染有明显差异遇到复杂模板要专门调字体和 tblHeader 属性投入时间并不少。我给一个项目做选型时测过一组数据40 页的合同模板docx4j 第一次转换耗时接近 90 秒且页面右下角有一个表格边框渲染错位需要写样式补丁soffice 第一次启动大约 15 秒后续保持在 4 到 6 秒。从那以后我的建议就固定成能用无头 office 的场景不用纯 Java 库硬扛。3.2 Linux 服务器上的无头转换命令参数与进程控制无头转换的正确姿势不是直接 Runtime.getRuntime().exec() 一把梭而是用 ProcessBuilder把参数、工作目录、超时都管起来。转换命令里最容易被忽略的是 -env:UserInstallation这个参数指定办公套件的用户配置目录。不设置时它会默认读当前系统用户的 HOME 下的配置目录遇到并发批量转换或者 Jenkins 这种以服务账号运行的环境两个进程可能同时抢写同一个配置文件轻则卡十几秒重则直接报已存在锁文件。private int convertDocxToPdf(String srcDocx, String outDir) throws IOException, InterruptedException { ListString cmd new ArrayList(); cmd.add(soffice); cmd.add(--headless); cmd.add(--invisible); cmd.add(--norestore); cmd.add(-env:UserInstallationfile:/// System.getProperty(java.io.tmpdir) .replace(\\, /) /lo_profile); cmd.add(--convert-to); cmd.add(pdf:writer_pdf_Export); cmd.add(--outdir); cmd.add(outDir); cmd.add(srcDocx); ProcessBuilder pb new ProcessBuilder(cmd); pb.redirectErrorStream(true); Process process pb.start(); if (process.waitFor(120, TimeUnit.SECONDS)) { return process.exitValue(); } process.destroyForcibly(); return -1; }参数逐个说--headless 让程序不启动图形界面--invisible 进一步隐藏启动闪屏和向导--norestore 防止它去恢复上次意外关闭时的文档列表。这些参数在无桌面环境中缺一不可尤其是集成系统在 root 账号下运行为 root 时缺一个就可能弹出一个看不到的对话框然后进程挂死。--convert-to 后面跟的 pdf:writer_pdf_Export 是过滤条件明确的 writer 导出器比裸写 pdf 更可控。srcDocx 和 outDir 都必须传绝对路径因为 ProcessBuilder 默认继承当前工作目录你用相对路径时找不到文件还不报错输出文件会落在奇怪的位置。还有一个服务器层面的常见问题纯 headless 环境通常没有 X serversoffice 虽然自称 headless但在某些发行版上启动还是会因为缺少 X 库而静默失败。稳定的做法是用 xvfb-run -a 包一层让整个命令变成 xvfb-run -a soffice --headless --convert-to pdf ...。我排查过不下三次代码没报错、PDF 没生成的问题最后都落在缺 xvfb-run 上。3.3 两条常见路线的适用边界维度无头办公套件转 PDFdocx4j 转 PDF版式保真高接近办公软件渲染效果中复杂表格和文本框容易错位中文字体依赖服务器字体库可配置 alias依赖 FOP 的字体注册配置繁琐部署成本需要安装外部程序加 xvfb纯 Java 依赖无需外部程序并发能力必须隔离 profile 目录才能并发线程安全单 JVM 内可控适合场景合同、标书、复杂模板 PDF 生成轻量模板、可接受版式微差的后台任务这个表格不完全代表谁的绝对优劣而是提醒你选型先问自己三个问题模板是不是有大量表格、页眉、页脚域服务器上是否允许安装外部软件转换的并发量级是每秒几次还是每分钟几次。答案决定了路线别一上来就纠结 API 细节。4. 落地一条完整链路模板填值、补页码、转 PDF 一步拿到4.1 模板约定与替换策略占位符选用与字面替换这个方案能稳定跑起来前提是模板占位符格式统一。我在团队里定的约定是 ${name} 风格理由是它天然避开了中文文本里常见的{}符号而且在数据替换时用 String.replace 做字面匹配不会跟正则语法打架。如果模板里既有 ${name} 又有 ${name}打错成中文括号程序里宁可报错也不要静默放过否则数据会带着占位符一起走进 PDF。替换之前要把用户传入的数据做一次规整null 转成空字符串Long 和 BigDecimal 统一按业务格式输出。有一个坑是金额类字段模板里写 {amount} 元数据是 12345替换后变成 12345 元没毛病可要是 12345.0 就会出现.0漏出去。我一般会在组装 map 之前先对金额做格式化确定保留几位小数再放进替换 map。4.2 完整服务代码从 docx 临时文件到 PDF 落盘下面的代码是一个最小可用的 Template2PdfService它把前面章节的替换函数和转换命令串成一条链。核心顺序是读模板 → 内存中替换 → 补页脚页码 → 写到临时 docx → 调 soffice 转 PDF → 移动 PDF 到目标路径。为什么中间必须有一个临时 docx因为 soffice 转换的输入必须是磁盘上的独立文件你不可能让外部进程读取 JVM 内存里的 XWPFDocument 对象而且如果直接覆盖原模板文件万一转换失败原模板就毁了一份能用原始模板重新生成的后悔药都没有。public class Template2PdfService { public void execute(Path templatePath, Path targetPdf, MapString, String variables) throws Exception { Path tmpDocx Files.createTempFile(contract-, .docx); try (InputStream in Files.newInputStream(templatePath); XWPFDocument document new XWPFDocument(in)) { DocxTextReplacer.replaceAllInDocx(document, variables); addPageNumberFooter(document); try (OutputStream out Files.newOutputStream(tmpDocx)) { document.write(out); } } String tmpOutDir Files.createTempDirectory(pdf-out-).toString(); String profileDir Files.createTempDirectory(lo-profile-).toString(); int code convertDocxToPdf(tmpDocx.toString(), tmpOutDir, profileDir); if (code ! 0) { throw new IllegalStateException(soffice convert failed, exit code); } String pdfName tmpDocx.getFileName().toString().replace(.docx, .pdf); Path generatedPdf Path.of(tmpOutDir, pdfName); Files.move(generatedPdf, targetPdf, StandardCopyOption.REPLACE_EXISTING); } }这段代码的完整链路依赖前面两个方法replaceAllInDocx 负责替换convertDocxToPdf 负责调外部命令。这里我把 profileDir 也传进去了是为了确保每个任务独立配置文件目录上面 3.2 节里的方法签名要对应改成接收 profileDir 参数。注意生成 PDF 的文件名由 tmpDocx 的文件名决定它是随机前缀所以移动时直接用文件名做拼接不要尝试去推断业务合同号。4.3 顺带补页码域POI 写 PAGE 域的极简方式如果模板没有页码而交付的 PDF 又必须每页底部有第 X 页可以在替换后动态创建一个默认页脚并插入 PAGE 域。POI 没有直接封装 Word 的域对象要碰底层 XML代码不算多但容易在命名空间上绊脚依赖版本不同类的位置也有差异。private void addPageNumberFooter(XWPFDocument document) { XWPFFooter footer document.createFooter(HeaderFooterType.DEFAULT); XWPFParagraph paragraph footer.createParagraph(); paragraph.setAlignment(ParagraphAlignment.CENTER); paragraph.createRun().setText(第 ); XWPFRun pageRun paragraph.createRun(); CTR ctr pageRun.getCTR(); // 这里用到 openxmlformats 包下的 STFldCharType ctr.addNewFldChar().setFldCharType(STFldCharType.BEGIN); ctr.addNewInstrText().setStringValue( PAGE ); ctr.addNewFldChar().setFldCharType(STFldCharType.END); paragraph.createRun().setText( 页); }页脚的 PAGE 域不是 POI 算出来的是 soffice 在转换时读 docx 的字段指令并渲染成的实际页码。所以你只要把指令结构写对就行。注意 createFooter 会返回一个新的页脚对象如果模板里已经有默认页脚再 create 一个会形成冲突稳妥做法是先判断 getFooterList().size()非空就取第一个页脚往里面追加段落。另外低版本 POI 的 ooxml-schemas 精简包可能没有 CTFldChar 的完整实现遇到编译报错时需要把依赖换成 poi-ooxml-full这个属于依赖环境踩坑详见下一章的避坑记录。5. 避坑记替换转 PDF 最容易翻车的五个现场5.1 现象替换成功PDF 里还是旧文案原因通常有两个。第一你在内存里改了 document 对象但 write 的时候写到了别的路径soffice 读到的是旧文件第二soffice 转换使用的输入文件路径是相对路径进程工作目录和 JVM 工作目录不一致它打开的并不是你生成的那个临时文件。解决方法是强制遵循两个习惯所有中间文件用 Files.createTempFile 并保存绝对路径转换命令入参拼写绝对路径不要依赖当前目录。我后来在代码里加了断言转换前检查 tmpDocx 的 lastModified 是否大于模板文件的 lastModified如果比模板还旧就直接抛异常。5.2 现象PDF 中文变成方块这是服务器环境问题不是 POI 的问题。模板里指定的字体比如微软雅黑或宋体在服务器上没有安装办公套件找不到字形数据就渲染成占位方块。解决分两步先在本机执行 fc-list 看字体列表里是否有目标字体没有就安装如果模板用字体实在不好买可以用 fontconfig 的 alias 把缺省字体映射到思源黑体这类开源字体。还有一种隐蔽情况是模板里部分文本用了默认字体而不是明确指定渲染时套件选用本地默认字体不同环境得到不同效果。我的习惯是在模板里统一指定字体家族并且每次上线前用真实模板在目标服务器跑一个最小转换烟雾测试不看业务数据只看中文是否可读。5.3 现象替换后段落样式整段塌掉第 2 章提过整段拼接后写进第一个 run 会损失其他 run 的字体样式。现象是模板里把合同金额用红色加粗显示替换后红色没了或者整段字号变了。原因是 run 是保存格式的最小粒度你清空了其他 run 就等于删掉了它们的格式。解决方法是区分场景如果模板段落样式统一用整段替换如果模板有混排样式就只在包含占位符的 run 内替换跨 run 的占位符优先手工调整模板让它在一个 run 内闭合别让程序去猜模板作者的格式意图。这里我一般会提前跟模板制作者约定占位符前后不要手动换字体不要拆成多个文本块这样能把 90% 的样式坑消灭在模板阶段。5.4 现象替换后表格挤压或整体多出大半页模板里表格通常设置成自动列宽Word 根据内容动态换行。你把 {amount} 替换成一串很长的数字表格按新内容重新计算列宽挤得整个版面变丑。原因是 POI 只改文本不重新排版渲染由办公套件完成版式变化是正常反应。解决方法分两个层面模板层面给表格关键列设置固定列宽甚至用拆分单元格的方式限制内容宽度代码层面替换后不处理表格几何属性因为 POI 的宽度单位是 dxa直接改数值很容易出现各列宽度之和超过页面可用宽度的情况除非你能精确计算否则别碰。我遇到这种需求时一般回退到模板编辑器里把列宽改成固定模式让业务填固定长度数据比在代码里写死宽度可靠得多。5.5 现象soffice 批量转文件卡死越跑越慢并行调用 soffice 时如果没有给每个进程独立的 -env:UserInstallation多个进程会争抢同一个用户配置文件目录表现是第一个转换特别慢后续任务全部挂住进程列表里躺着几十个 soffice。解决方法是每次转换前创建一个临时 profile 目录转换结束后删除或保留复用。另一个共性问题是没有 xvfb 支持时soffice 启动到一半因为没有显示环境而挂起命令没有任何输出只能通过 ps 看到进程不退出。解决方法是包一层 xvfb-run -a或者用 soffice 提供的 --nologo --nodefault 参数组合但最稳妥还是 xvfb。把这些写进启动脚本后我的批量任务从偶尔超时变成了稳定转换成本就是多装一个工具包。6. 进阶把验证和断言接进流水线结束“转换玄学”6.1 PDFBox 文本断言让 PDF 里有没有替换值变成可测试的转换完成后别急着打开 PDF 人工看先用 PDFBox 抽取 PDF 文本断言关键占位符已经被替换成目标值。这一步能拦住绝大多数页眉没替换某条数据遗漏的问题比肉眼扫十页 PDF 靠谱得多。private static boolean assertPdfContains(Path pdf, String... expectedValues) throws IOException { try (PDDocument document PDDocument.load(pdf.toFile())) { PDFTextStripper stripper new PDFTextStripper(); String content stripper.getText(document); for (String value : expectedValues) { if (!content.contains(value)) { return false; } } return true; } }这个断言方法可以直接放进单测每次改模板后跑一遍。验证文本里如果有中文建议把 content 和 expectedValues 都做一下 NFKC 归一化再去 contains否则某些全角字符差异会误报。页面数也可以断言把 PDDocument 的 getNumberOfPages() 和模板预期页数做比较如果某次替换导致文本暴涨、多出几页页面数断言能立刻暴露。6.2 批处理时让缺失占位符显形替换计数与日志批量生成几十份 PDF 时只有一个断言还不够你要知道每份文档里到底成功替换了几个占位符、有没有 key 没命中。我在 replaceAllInDocx 方法里加了一个计数器返回替换命中数任何一份文档命中数低于 map 里的 key 数量时就把文档名和缺失 key 记进日志。这样即使某份文档因为模板被手工改过而缺少占位符你也能马上知道是哪一份、缺了哪个而不是等业务方截图反馈。从那段连续踩坑的日子以后我每做一套 docx 替换转 PDF 的系统都把替换计数、PDF 文本断言、页面数校验三条固定接进流水线。收到模板先跑空值烟雾测试再跑真实样本比对转换命令的 profile 目录也强制独立创建。希望这份笔记能帮你少走这几段弯路把生成 PDF 这件看着简单做起来碎的事变成一条稳定可交付的流水线。本文还有配套的精品资源点击获取
RELATED

相关推荐

ComfyUI FluxRedux换装实战:蒙版抠图与参数调优避坑指南

ComfyUI FluxRedux换装实战:蒙版抠图与参数调优避坑指南

简介:这份资源面向使用 ComfyUI 进行 AI 绘画与换装工作流的进阶用户,聚焦 FluxRedux 与蒙版(Mask)结合的 OOTD 基础换装场景,帮助解决人物服装局部替换时区域控制不精准、蒙版与重绘衔接不自然等问题。压缩包内共 1 个…

📅 2026/10/11 22:47:18
基于SpringBoot的高校毕业生求职就业服务平台设计与实现

基于SpringBoot的高校毕业生求职就业服务平台设计与实现

先说结论:这个标题里的几个名字,其实都是同一类系统——基于SpringBoot的高校毕业生求职就业服务平台。不管叫“就业管理系统”还是“校园招聘人才供需智能匹配系统”,核心都是解决一件事:让高校学生找工作、用人单位招人、辅导员…

📅 2026/10/11 22:47:18
REA模型:用资源-事件-参与者重构业务系统,告别状态字段混乱

REA模型:用资源-事件-参与者重构业务系统,告别状态字段混乱

1. 别被会计术语劝退:REA 模型到底在描述什么如果只给我一个词——rea,我先问自己一句:这到底是个项目代号,还是某个缩写的残片?说实话,我第一次接触 REA(Resource-Event-Agent,资源…

📅 2026/10/11 22:47:18
MORE NEWS

更多资讯

📰

如何把“无可挑剔”变成可执行的工作清单?标准定义与流程复盘实战

“impeccable”这个词,我盯着看了很久。它不像是那种一眼就能猜出功能的标题,恰恰是这种“不直白”,让我当时决定把这个项目做下去。一年多时间,从零开始摸索到产出稳定,今天想把这个过程中关于“如何把一件事做到无可…

📰

Web 3D 实例化网格(InstancedMesh)几何合批:用单次 Draw Call 渲染森林与千级手办

在构建大型三维元宇宙展厅、大促虚拟 3D 商城街区、以及自然生态数字孪生(如漫山遍野的植被树木、飘落的花瓣)时,前端 3D 工程师最先撞上的“性能叹息之墙”,往往不是 GPU 的多边形光栅化能力,而是CPU 侧的绘制调用过载…

📰

Cursor怎么使用:3分钟上手Cursor键盘快捷键速查,用TaoToken统一Key接入GPT4与Claude 3.5辅助编程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

我喜欢的 VSCode 插件:用 TaoToken 统一管理 AI 编码助手的 Key 与 Base URL

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

企业 Agent 提示词注入防御实战:双重护栏与对抗语义检测

在企业将多智能体(Multi-Agent)系统接入客服咨询、内部知识检索或自动化办公流后,安全攻防的对抗维度发生了一场根本性范式转移:传统的 SQL 注入或跨站脚本攻击(XSS)正在退居二线,而以自然语言为…

📰

【无功优化】基于遗传算法和改进的遗传算法求解电力系统无功优化问题研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和数学建模资料 &…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬