如何使用PDF.js保存带注释的PDF文件?求实现方案
使用PDF.js保存带注释的PDF到后端的解决方案
PDF.js完全支持将添加注释后的PDF保存到后端,你的问题主要出在几个细节处理上,下面是具体的排查和修正方案:
现有代码的关键问题分析
- Axios的Content-Type设置错误:手动指定
Content-Type: multipart/form-data会覆盖浏览器自动生成的边界标识,导致后端无法解析文件。 - PDF加载时未启用编辑权限:如果使用官方的
viewer.html嵌入iframe,需要确保URL参数开启注释编辑功能,否则saveDocument可能无法生效。 - 注释序列化时机可能过早:
pagesloaded事件仅表示页面加载完成,但注释编辑器层可能尚未完全初始化,导致序列化的注释不完整或无效。
修正后的完整代码
<script> document.addEventListener('DOMContentLoaded', () => { const iframe = document.getElementById('iframe_pdfjs'); // 确保viewer.html启用注释编辑(URL参数添加) iframe.src = '/path/to/pdfjs/web/viewer.html?file=/your-document.pdf&enableAnnotationEditor=true&editable=true'; iframe.onload = function() { const iframeWindow = iframe.contentWindow; // 监听annotationEditorReady事件,确保注释编辑器完全初始化 iframeWindow.addEventListener('annotationEditorReady', function() { console.log('PDF.js annotation editor ready'); const annotationStorage = iframeWindow.PDFViewerApplication.pdfViewer.annotationStorage; if (!annotationStorage) { console.error('Annotation storage not available'); alert('Annotations could not be retrieved.'); return; } document.getElementById('saveForm').addEventListener('submit', async function(event) { event.preventDefault(); try { const pdfDoc = iframeWindow.PDFViewerApplication.pdfDocument; // 序列化当前所有注释为XFDF格式 const annotations = annotationStorage.serialize(); if (!annotations) { console.error('Annotations are null or undefined'); alert('Failed to serialize annotations.'); return; } // 调用saveDocument生成带注释的PDF二进制数据 const pdfData = await pdfDoc.saveDocument({ xfdfString: annotations }); // 直接使用返回数据创建Blob const blob = new Blob([pdfData], { type: 'application/pdf' }); console.log('Blob created:', blob); console.log('Blob size:', blob.size); const formData = new FormData(); formData.append('signed_document', blob, 'signed_document.pdf'); // 添加表单字段 formData.append('first_name', document.getElementById('id_first_name').value); formData.append('last_name', document.getElementById('id_last_name').value); formData.append('email', document.getElementById('id_email').value); formData.append('phone', document.getElementById('id_phone').value); formData.append('dob', document.getElementById('id_dob').value); formData.append('gender', document.getElementById('id_gender').value); // 移除手动设置的Content-Type,让浏览器自动处理 const response = await axios.post('.', formData, { headers: { 'X-CSRFToken': '{{ csrf_token }}' } }); if (response.status === 200) { window.location.href = '{% url "sign:thank_you" %}'; } else { alert('Failed to save the signed document.'); } } catch(error) { console.error('Save error:', error); alert('Failed to save the signed document.'); } }); }); }; }); </script>
关键修正点说明
- 启用注释编辑:在iframe的src中添加
enableAnnotationEditor=true&editable=true参数,确保PDF.js允许编辑和保存注释。 - 监听正确的初始化事件:改用
annotationEditorReady事件替代pagesloaded,确保注释编辑器完全就绪。 - 修复Axios请求头:删除手动设置的
Content-Type,FormData会自动生成正确的请求头(包含边界信息),后端才能正确解析文件。 - 简化Blob创建:
saveDocument返回的pdfData通常已经是Uint8Array或ArrayBuffer,无需额外转换,直接传入Blob即可。
最佳实践
- 避免跨iframe直接访问内部对象:如果生产环境中存在跨域风险,建议使用
postMessage与PDF.js viewer通信,传递注释数据和保存指令,更安全且兼容。 - 验证注释有效性:在调用
saveDocument前,可通过annotationStorage.getAnnotations()检查是否有有效注释,避免生成空注释的PDF。 - 后端接收验证:确保后端正确处理
multipart/form-data请求,验证文件字段名signed_document是否匹配,并且检查文件MIME类型和大小。 - PDF.js版本兼容性:不同版本的PDF.js API可能有差异,建议使用稳定版(如v3.x或v4.x),并参考官方文档确认
saveDocument的参数格式。
内容的提问来源于stack exchange,提问作者antonnifo
相关产品推荐
相关产品推荐

