You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PDF.js保存带注释的PDF文件?求实现方案

使用PDF.js保存带注释的PDF到后端的解决方案

PDF.js完全支持将添加注释后的PDF保存到后端,你的问题主要出在几个细节处理上,下面是具体的排查和修正方案:

现有代码的关键问题分析

  1. Axios的Content-Type设置错误:手动指定Content-Type: multipart/form-data会覆盖浏览器自动生成的边界标识,导致后端无法解析文件。
  2. PDF加载时未启用编辑权限:如果使用官方的viewer.html嵌入iframe,需要确保URL参数开启注释编辑功能,否则saveDocument可能无法生效。
  3. 注释序列化时机可能过早: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>

关键修正点说明

  1. 启用注释编辑:在iframe的src中添加enableAnnotationEditor=true&editable=true参数,确保PDF.js允许编辑和保存注释。
  2. 监听正确的初始化事件:改用annotationEditorReady事件替代pagesloaded,确保注释编辑器完全就绪。
  3. 修复Axios请求头:删除手动设置的Content-Type,FormData会自动生成正确的请求头(包含边界信息),后端才能正确解析文件。
  4. 简化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 01:21:02