React 项目使用 pdf.js 及 Elasticpdf 教程
摘要:本文章介绍如何在 React 中使用 pdf.js 及基于 pdf.js 的批注开发包 Elasticpdf。简单 5 步可完成集成部署,包括数据的云端同步,示例代码完善且简单,文末有集成代码分享。

1. 工具库介绍与 Demo
1.1 代码包结构
ElasticPDF基于开源 pdf.js (Demo地址:https://mozilla.github.io/pdf.js/web/viewer.html),增加了多种开箱即用的 PDF 批注功能。代码包延续了 pdf.js-dist 独立且完全离线的结构风格,仅增加了用于支持批注的离线 Javascript 代码,与 pdf.js-dist 一样可以快速完美集成到任何可以运行Javascript, HTML, CSS 的项目环境中,在公网及内网环境下运行。

1.2 Elasticpdf 在线 Demo
根据不同的功能及预算需求,有两个版本的产品可供选择,两者仅在最终的批注保存阶段有区别,产品 Demo 地址如下:
① 批注合成版: https://demos.libertynlp.com/#/pdfjs-annotation
② 专业批注版: https://www.elasticpdf.com/demo
2. 移动至 React 项目
移动 pdf.js 或 Elasticpdf 代码包到 React 项目的 public 文件夹下。

pdf.js 成功导入 React 快照

3. 导入 viewer.html
① 通过 <iframe> 导入 elasticpdf 或 pdf.js 代码包中的 viewer.html 文件,注意路径不要写错。
<!-- elasticpdf 示例 -->
<iframe id='elasticpdf-iframe' onLoad={initialPDFEditor} src='elasticpdf/web/viewer.html' style={{width:"100%",border:0,height:"700px"}}></iframe><!-- pdf.js 示例 -->
<iframe id='elasticpdf-iframe' onLoad={initialPDFEditor} src='pdfjs-3.2/web/viewer.html' style={{width:"100%",border:0,height:"700px"}}></iframe>
② 将 web 文件夹下 viewer.js 中 defaultUrl 默认值置空,否则在第 ① 步中导入 viewer.html 时会默认加载 compressed.tracemonkey-pldi-09.pdf 文件,影响自定义加载文件的流程。Elasticpdf 代码包中的 viewer.js 已默认修改完成。
// 原 defaultUrl 默认值
defaultOptions.defaultUrl = {value: "compressed.tracemonkey-pldi-09.pdf",kind: OptionKind.VIEWER
};// 置空后默认值
defaultOptions.defaultUrl = {value: "",kind: OptionKind.VIEWER
};
③ 在 React 页面中的 <iframe> onLoad() 函数下调用 initialApp() 函数,由于 pdf.js 和 elasticpdf 中的函数都是在 iframe 的作用域下,因此必须在 iframe load结束后再初始化,否则无法获取 iframe 的contentWindow,也就无法调用其中的函数。
var elasticpdf_viewer = null;
function initialPDFEditor() {listenPDFEditorMessage();elasticpdf_viewer = document.getElementById('elasticpdf-iframe').contentWindow;console.log('elasticpdf_viewer', elasticpdf_viewer);var pdf_url="compressed.tracemonkey-pldi-09.pdf";elasticpdf_viewer.initialApp({'language': 'zh-cn', // 交互语言'pdf_url': pdf_url,'member_info': { //用户信息'id': 'elasticpdf_id','name': 'elasticpdf',},});
}// 监听 pdf 编辑等各种信息的回调
function listenPDFEditorMessage() {window.addEventListener('message', (e) => {if (e.data.source !== 'elasticpdf') {return;}// pdf 加载结束的回调,可以在此处导入服务器上储存的批注文件if (e.data.function_name === 'pdfLoaded') {console.log('PDF加载成功');reloadData();}});
}
④ pdf.js 初始化函数如下,主要内容为调用 PDFViewerApplication.open() 打开传入的文档链接,并使用 loadPdf() 函数监听文档是否初始化结束,最后通过 postMessage 广播加载状态至 React 页面。
<script type='text/javascript'>//初始化函数function initialApp(paras) {var oriUrl=paras['pdf_url'];PDFViewerApplication.open(oriUrl);interval = setInterval('loadPdf()', 1000);}//监听文档是否初始化完成var interval = null;function loadPdf() {if (PDFViewerApplication.pdfDocument == null) {console.info('Loading...');} else {//文档初始化完成console.log('PDF Load successfully');clearInterval(interval);//广播信息postPDFData("pdfLoaded", '');}}//广播 pdf.js 操作状态信息function postPDFData(function_name,new_content){window.parent.postMessage({"type":0,"source":"elasticpdf",'function_name':function_name,"content":new_content},'*');window.postMessage({"type":0,"source":"elasticpdf",'function_name':function_name,"content":new_content},'*');}
</script>
⑤ 需要注意的是 pdf.js 端和存放 pdf 文件的都要支持跨域,否则会报 CORS 跨域错误。具体来说如果服务器是通过 Java 或者 Python 等程序提供文档,则需要在程序中允许跨域;而如果是 nginx 服务器,则在配置中可以如下设置。
location / {add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Headers *;add_header Access-Control-Expose-Headers Accept-Ranges,Content-Range;add_header Accept-Ranges bytes;
}
对于 pdf.js 端的跨域,需要在 elasticpdf 或 pdf.js 的 viewer.js 中搜索 HOSTED_VIEWER_ORIGINS 并加入域名。
const HOSTED_VIEWER_ORIGINS = ["null", "http://mozilla.github.io", "https://mozilla.github.io"];
4. 导出 pdf 及批注数据
Elasticpdf 所生成批注数据的保存有两种方式,我们推荐方式二。pdf.js 默认将批注写入文档,无法分离保存。
4.1 方式一:批注写入PDF
将批注写入到 pdf 中然后下载整个文档,一般用户可以通过Ctrl+S快捷键和 UI 按钮来完成,这种方式完全不需要后端服务的支持。
在需要保存批注后 pdf 至服务器的场景中,可以通过如下代码实现。
// 绑定该函数至 dom 用于触发 pdf 保存
function getPDFData() {elasticpdf_viewer.getPDFData();
}// 接收pdf数据并且上传至服务器
window.addEventListener('message', (e) => {if (e.data.source != 'elasticpdf') {return;}// 接收pdf数据if (e.data.function_name == 'downloadPDF') {let file_name = e.data.content['file_name'];let pdf_blob = e.data.content['pdf_blob'];let pdf_base64 = e.data.content['pdf_base64'];// 接收到 pdf 数据,其中 pdf_base64 字符串数据可以快捷上传到服务器postService('upload-pdf-data', {'file_name':file_name,'file_id':'123ddasfsdffads','file_data':pdf_base64,});}
});
4.1 方式二:批注单独保存
针对云端同步批注的需求,单独将批注文件导出为JSON文件,传输并保存于服务器,之后加载回显后可继续编辑批注。
这样的方式仅需一个在线PDF原文件,只传输很小体积的批注(通常不到 1M 大小),可以节约很多的存储和宽带费用。
// 在 pdf 批注编辑后的回调函数中可以读取所有批注文件并且上传至服务器
window.addEventListener('message', (e) => {if (e.data.source != 'elasticpdf') {return;}// pdf 批注编辑回调,可以在此处导出批注并传输到服务器if (e.data.function_name == 'annotationsModified') {// 仅获取 pdf 批注文件,不写入到 pdf 中let this_data = elasticpdf_viewer.pdfAnnotation.outputAnnotations();let annotation_content = JSON.stringify(this_data['file_annotation']);let file_name = this_data['file_name'];postService('upload-annotation-data', {'file_name':file_name,'file_id':'123ddasfsdffads','file_annotation':annotation_content,});}
});
5. 重载 pdf 及批注数据
单独将 pdf 批注保存至服务器后,可以在加载 pdf 文件后再次从服务器中下载批注并且重载回显到 pdf 上继续编辑。
// 在 pdf 加载完成后的回调中可以从服务器请求相应的批注并重载于 pdf 上。
window.addEventListener('message', (e) => {if (e.data.source != 'elasticpdf') {return;}// pdf 加载完成的回调,可以在此处导入服务器上储存的批注文件if (e.data.function_name == 'pdfLoaded') {let file_name = 'tutorial.pdf'let annotation_content =await postService('get-annotation-data', {'file_name':'tutorial.pdf','file_id':'123ddasfsdffads',});// 批注重载回显于当前文件elasticpdf_viewer.setPureFileAnnotation({'file_annotation': annotation_content});}
});
以上的所有与服务器的交互需要前后端协同,后端服务器需要响应程序来接收和保存数据,对于 Elasticpdf 的用户我们有简单的 PHP、Python 及 Java 代码示例供参考。
前端发起请求的示例函数 postService() 代码如下。
// 与后端服务器进行网络通信的函数
async function postService(url, data) {var new_data = new URLSearchParams();var encrpte_data = data;new_data.append('data', encrpte_data);var base_url = "your-server-url";var posturl = base_url + url;const response = await fetch(posturl, {method: 'POST',headers: {},body: new_data, });const resp = await response.json();resp['data'] = JSON.parse(resp['data']);return resp;
}
总结
至此,pdf.js 及 elasticpdf 集成于 React 项目的代码完毕,带有 pdf.js 代码包的 React 示例项目包内容已上传至 Github(网址:https://github.com/ElasticPDF/React-use-pdf.js-elasticpdf),可以直接下载。Elasticpdf 客户如有其他应用场景需求欢迎联系我们,我们将为您提供示例代码。
温馨提示:本文首发于 https://www.elasticpdf.com ,转载请注明出处:https://www.elasticpdf.com/blog/react-use-pdfjs-and-elasticpdf-tutorial-zh.html
相关文章:
React 项目使用 pdf.js 及 Elasticpdf 教程
摘要:本文章介绍如何在 React 中使用 pdf.js 及基于 pdf.js 的批注开发包 Elasticpdf。简单 5 步可完成集成部署,包括数据的云端同步,示例代码完善且简单,文末有集成代码分享。 1. 工具库介绍与 Demo 1.1 代码包结构 ElasticP…...
性能测试之jmeter的基本使用
简介 Jmeter是Apache的开源项目,基于Java开发,主要用于进行压力测试。 优点:开源免费、支持多协议、轻量级、功能强大 官网:https://jmeter.apache.org/index.html 安装 安装步骤: 下载:进入jmeter的…...
CAD插件实现:所有文字显示到列表、缩放、编辑——CAD-c#二次开发
当图中有大量文字,需要全部显示到一个列表时并缩放到需要的文字时,可采用插件实现,效果如下: 附部分代码如下: private void BtnSelectText_Click(object sender, EventArgs e){var doc Application.DocumentManager.…...
【Git】“warning: LF will be replaced by CRLF”的解决办法
一、原因分析 不同操作系统的换行符标准不同: • Windows:使用 CRLF(\r\n)表示换行; • Linux/Mac:使用 LF(\n)表示换行 Git 检测到本地文件的换行符与仓库设置或目标平台不兼容时…...
vue2(13) 组件注册的学习笔记
文章目录 Vue2 组件注册学习笔记一、组件注册的基本概念二、全局注册三、局部注册四、组件名的命名规范五、在模块系统中注册组件六、基础组件的自动化全局注册七、总结Vue2 组件注册学习笔记 一、组件注册的基本概念 在Vue中,组件是可复用的Vue实例,具有封装的模板和逻辑。…...
【spring Cloud Netflix】OpenFeign组件
1.概述 Feign旨在使编写Java Http客户端变得更容易。前面在使用RibbonRestTemplate进行服务的远程调用 时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由 于对服务的依赖调用可不止一处࿰…...
go游戏后端开发20:房间消息推送处理
创建房间逻辑前的概念梳理及代码实现 在编写创建房间的逻辑之前,我们需要先创建几个关键概念。 第一个概念是“联盟”。联盟可以理解为一个组织,它持有多个房间。一个联盟下可能挂载多个房间,这是我们的第一个概念。 第二个概念是“管理”…...
Oracle数据库数据编程SQL<8 文本编辑器Notepad++和UltraEdit(UE)对比>
首先,用户界面方面。Notepad是开源的,界面看起来比较简洁,可能更适合喜欢轻量级工具的用户。而UltraEdit作为商业软件,界面可能更现代化,功能布局更复杂一些。不过,UltraEdit支持更多的主题和自定义选项&am…...
Linux驱动开发练习案例
1 开发目标 1.1 架构图 操作系统:基于Linux5.10.10源码和STM32MP157开发板,完成tf-a(FSBL)、u-boot(SSBL)、uImage、dtbs的裁剪; 驱动层:为每个外设配置DTS并且单独封装外设驱动模块。其中电压ADC测试,采用linux内核…...
Windows程序中计时器WM_TIMER消息的使用
本文章是对《Windows程序设计》这本书第八章计时器的总结,如果有时间,可以去看书里的讲解,如果时间不充裕,想马上知道计时器该如何使用,欢迎阅读本文,本文已经将计时器的干货整理完毕! 什么是计…...
Apache httpclient okhttp(1)
学习链接 Apache httpclient & okhttp(1) Apache httpclient & okhttp(2) httpcomponents-client github apache httpclient文档 apache httpclient文档详细使用 log4j日志官方文档 【Java基础】- HttpURLConnection…...
微信小程序—路由
关于 app.json 中的配置 app.json 主要是对整个小程序进行一个全局的配置。 pages:在这个配置项目中,就可以配置小程序里面的页面,小程序默认显示 pages 数组中的第一个页面windows:主要配置和导航栏相关的 当然,在…...
人工智能驱动的数据仓库优化:现状、挑战与未来趋势
1. 引言:数据仓库的演进与人工智能驱动优化的兴起 现代数据仓库的复杂性和规模正以前所未有的速度增长,这主要是由于数据量、种类和产生速度的急剧增加所致。传统的数据仓库技术在应对这些现代数据需求方面显得力不从心,这催生了对更先进解决…...
LVS高可用负载均衡
一、项目图 二、主机规划 主机系统安装应用网络IPclientredhat 9.5无NAT192.168.72.115/24lvs-masterredhat 9.5ipvsadm,keepalivedNAT192.168.72.116/24 VIP 192.168.72.100/32lvs-backupredhat 9.5ipvsadm,keepalivedNAT192.168.72.117/24 VIP 192.168…...
脑影像分析软件推荐 | JuSpace
目录 1. 软件界面 2.工具包功能简介 3.软件安装注意事项 参考文献: Dukart J, Holiga S, Rullmann M, Lanzenberger R, Hawkins PCT, Mehta MA, Hesse S, Barthel H, Sabri O, Jech R, Eickhoff SB. JuSpace: A tool for spatial correlation analyses of magne…...
集合框架——常用类
集合框架的理解 就是一个常用类 集合主要有三种类型【集合与集合之间可以互转】 List(列表,是接口)Set(集合,是接口)Map(键值对,是接口)Collection是以上及所有集合的根接口,其里面的子类,一部分支持集合有序元素可重复,一部分支持集合无序元素不重复 可重复和不…...
【NLP 面经 7、常见transformer面试题】
目录 1. 为何使用多头注意力机制? 2. Q和K使用不同权重矩阵的原因 3. 选择点乘而非加法的原因 4. Attention进行scaled的原因 5. 对padding做mask操作 6. 多头注意力降维原因 7. Transformer Encoder模块简介 8. 乘以embedding size的开方的意义 9. 位置编码 10. 其…...
分布式事务解决方案全解析:从经典模式到现代实践
前言 在分布式系统中,数据一致性是一个核心问题。随着微服务架构的普及,跨服务、跨数据库的操作变得越来越普遍,如何保证这些操作的原子性、一致性、隔离性和持久性(ACID)成为了一个极具挑战性的任务。本文将全面介绍…...
软件工程面试题(二十七)
1、j a v a 对象初始化顺序 1.类的初始化(initialization class & interface) 2.对象的创建(creation of new class instances) 顺序:应为类的加载肯定是第一步的,所以类的初始化在前。大体的初始化顺序是: 类初始化 -> 子类构造函数 -> 父类构造函数 -&g…...
fastGPT—nextjs—mongoose—团队管理之部门相关api接口实现
创建部门或者子部门 import type { NextApiRequest, NextApiResponse } from next; import { NextAPI } from /service/middleware/entry; import { MongoOrgModel } from fastgpt/service/support/permission/org/orgSchema;async function handler(req: NextApiRequest, res…...
C++ 数据竞态检查
-fsanitizethread 编译时,添加参数-fsanitizethread -g,可以运行态检查数据竞态问题,包括: 数据竞态死锁锁、条件变量错误使用 check_tsan 开源库 yalantinglibs有段检查编译器是否支持 fsanitize 编译参数的宏,挺…...
逛好公园的好处
逛公园和软件开发看似是两个不同的活动,但它们之间存在一些有趣的关联和相互促进的关系: 激发创造力:公园中的自然景观、多样的人群以及各种活动能为开发者带来新的灵感和创意。软件开发过程中,从公园中获得的创意可以帮助开发者设…...
C++开发工具全景指南
专业编译与调试工具深度解析 2025年4月 编译器套件 GNU Compiler Collection (GCC) GNU编译器套件是自由软件基金会开发的跨平台编译器系统,支持C、C、Objective-C、Fortran、Ada等多种编程语言。作为Linux系统的标准编译器,GCC以其强大的优化能力和…...
【网络安全】 防火墙技术
防火墙是网络安全防御的重要组成部分,它的主要任务是阻止或限制不安全的网络通信。在这篇文章中,我们将详细介绍防火墙的工作原理,类型以及如何配置和使用防火墙。我们将尽可能使用简单的语言和实例,以便于初学者理解。 一、什么…...
文档的预解析
1. 预解析的核心目标 浏览器在正式解析(Parsing)HTML 前,会启动一个轻量级的 预解析器(Pre-Parser),快速扫描文档内容,实现: 提前发现并加载关键资源(如 CSS、JavaScrip…...
理解“功能内聚”
链接: 理解“偶然内聚” 理解“逻辑内聚” 理解“时间内聚” 理解“过程内聚” 理解“通信内聚” 理解“顺序内聚” 理解“功能内聚” 功能内聚(Functional Cohesion)是最高级别的内聚形式,指的是模块内的所有元素都紧密地围绕着一…...
windows 常用命令总结
工作中用到的 Linux 总结(持续更新中...)_linux工作经验-CSDN博客 PS: 推荐使用 powershell 而不是 cmd,因为PowerShell 是一个更先进和功能更强大的工具( powershell 有命令记忆功能,比较方便)…...
记一次表格数据排序优化(一)--排序30000条数据有多卡
目录 需求 第一次尝试 运行环境 思路 存储 排序 触发排序操作 如何实现高效的排序 关键1 关键2 关键3 磨刀不误砍柴工 关键4 代码 效果 卡顿原因分析 原因1 原因2 第二次尝试 需求 1 我的qt程序通过表格显示30000条数据。数据来自udp,udp每隔10秒…...
图形渲染中的定点数和浮点数
三种API的NDC区别 NDC全称,Normalized Device Coordinates Metal、Vulkan、OpenGL的区别如下: featureOpenGL NDCMetal NDCVulkan NDC坐标系右手左手右手z值范围[-1,1][0,1][0,1]xy视口范围[-1,1][-1,1][-1,1] GPU渲染的定点数和浮点数 定点数类型&a…...
【深度学习】CNN简述
文章目录 一、卷积神经网络(CNN)二、CNN结构特性1. CNN 典型结构2. 局部连接3. 权重共享4.空间或时间上的次采样 三、理解层面 一、卷积神经网络(CNN) 卷积神经网络(Convolutional Neural Network,CNN)是一种用于处理…...
