如何从src为PDF Blob的iframe中获取修改后的字节数据?
可行方案:替换原生PDF查看器为PDF.js
浏览器原生PDF查看器的iframe/embed是沙箱隔离的,无法直接访问其内部的表单修改数据,这是浏览器安全策略限制,没有绕过的办法。要实现需求,最靠谱的方式是用开源的PDF.js替代原生查看器,它能完全在浏览器端控制PDF文档,轻松获取修改后的表单数据。
核心思路
- 保留你现有流程的前3步:用pdf-lib加载模板、填充初始数据、生成字节流
- 放弃原生iframe,改用PDF.js渲染填充后的PDF,让用户在PDF.js的界面里修改表单
- 点击导出按钮时,通过PDF.js的API获取所有表单字段的当前值
- 用pdf-lib重新加载原始模板,填入修改后的数据,扁平化表单并下载
代码示例(关键部分)
首先引入PDF.js(建议本地部署,避免CDN跨域问题):
<script src="path/to/pdf.min.js"></script> <script src="path/to/pdf.worker.min.js"></script> <script src="https://unpkg.com/pdf-lib@latest"></script>
初始化PDF查看器并加载填充后的PDF
// 配置PDF.js的worker路径 pdfjsLib.GlobalWorkerOptions.workerSrc = 'path/to/pdf.worker.min.js'; async function initPDFViewer(originalTemplateBytes, initialFormData) { // 1. 用pdf-lib填充初始数据,得到预填充的PDF字节流 const prefilledPdfDoc = await PDFLib.PDFDocument.load(originalTemplateBytes); const form = prefilledPdfDoc.getForm(); Object.keys(initialFormData).forEach(fieldName => { const field = form.getField(fieldName); if (field) field.setText(initialFormData[fieldName]); }); const prefilledBytes = await prefilledPdfDoc.save(); // 2. 用PDF.js加载预填充的PDF const loadingTask = pdfjsLib.getDocument({ data: prefilledBytes }); const pdfDoc = await loadingTask.promise; // 3. 获取表单对象,PDF.js会自动渲染可编辑字段 const pdfForm = await pdfDoc.getForm(); // 将pdfDoc和pdfForm存到全局,方便后续导出时用 window.currentPdfDoc = pdfDoc; window.currentPdfForm = pdfForm; // 4. 渲染PDF到页面容器(这里用基础渲染,也可以用官方Viewer组件实现完整工具栏) const container = document.getElementById('pdf-container'); const page = await pdfDoc.getPage(1); const viewport = page.getViewport({ scale: 1.5 }); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; container.appendChild(canvas); await page.render({ canvasContext: context, viewport: viewport }).promise; }
获取修改后的数据并生成扁平化PDF
async function exportFlattenedPDF(originalTemplateBytes) { // 1. 通过PDF.js获取所有表单字段的当前值 const fields = await window.currentPdfForm.getFields(); const modifiedData = {}; for (const field of fields) { const fieldName = field.getName(); modifiedData[fieldName] = await field.getValue(); } // 2. 用pdf-lib重新加载原始模板,填充修改后的数据并扁平化 const finalPdfDoc = await PDFLib.PDFDocument.load(originalTemplateBytes); const finalForm = finalPdfDoc.getForm(); Object.keys(modifiedData).forEach(fieldName => { const field = finalForm.getField(fieldName); if (field) field.setText(modifiedData[fieldName]); }); // 扁平化表单(转为静态内容,不可编辑) finalForm.flatten(); // 3. 下载生成的PDF const finalBytes = await finalPdfDoc.save(); const blob = new Blob([finalBytes], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'final-flattened.pdf'; link.click(); URL.revokeObjectURL(url); }
关键注意事项
- PDF.js的worker文件必须和主文件同域,否则会出现跨域错误,建议本地部署而非CDN
- 如果需要完整的PDF查看功能(分页、缩放、工具栏),可以直接使用PDF.js官方的
viewer.html组件,集成更简单 - 扁平化操作必须基于原始模板重新生成,直接导出PDF.js的修改后文档可能仍保留可编辑字段,用pdf-lib的
flatten()方法能确保表单转为静态文本
内容的提问来源于stack exchange,提问作者Stefan Todoran
相关产品推荐
相关产品推荐

