React 项目使用 pdf.js 及 Elasticpdf 教程

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

1. 工具库介绍与 Demo

1.1 代码包结构

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

1.2 Elasticpdf 在线 Demo

根据不同的功能及预算需求,有两个版本的产品可供选择,两者仅在最终的批注保存阶段有区别,产品 Demo 地址如下:

① 批注合成版: demos.libertynlp.com/#/pdfjs-ann...

② 专业批注版: www.elasticpdf.com/demo

2. 移动至 React 项目

移动 pdf.js 或 Elasticpdf 代码包到 React 项目的 public 文件夹下。

pdf.js 成功导入 React 快照

3. 导入 viewer.html

① 通过 <iframe> 导入 elasticpdf 或 pdf.js 代码包中的 viewer.html 文件,注意路径不要写错。

javascript 复制代码
    <!-- 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.jsdefaultUrl 默认值置空,否则在第 ① 步中导入 viewer.html 时会默认加载 compressed.tracemonkey-pldi-09.pdf 文件,影响自定义加载文件的流程。Elasticpdf 代码包中的 viewer.js 已默认修改完成。

javascript 复制代码
    // 原 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结束可获取 contentWindow 后再调用。

javascript 复制代码
    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 页面。

javascript 复制代码
    <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 服务器,则在配置中可以如下设置。

javascript 复制代码
    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 并加入域名。

javascript 复制代码
    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 至服务器的场景中,可以通过如下代码实现。

javascript 复制代码
    // 绑定该函数至 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 大小),可以节约很多的存储和宽带费用。

javascript 复制代码
    // 在 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 上继续编辑。

javascript 复制代码
    // 在 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() 代码如下。

javascript 复制代码
// 与后端服务器进行网络通信的函数
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(网址:github.com/ElasticPDF/...),可以直接下载。Elasticpdf 客户如有其他应用场景需求欢迎联系我们,我们将为您提供示例代码。

温馨提示:本文首发于 www.elasticpdf.com ,转载请注明出处:www.elasticpdf.com/blog/react-...

相关推荐
我是哈哈hh1 小时前
【JavaScript进阶】作用域&解构&箭头函数
开发语言·前端·javascript·html
酷酷的阿云1 小时前
Vue3性能优化必杀技:useDebounce+useThrottle+useLazyLoad深度剖析
前端·javascript·vue.js
bin91531 小时前
DeepSeek 助力 Vue 开发:打造丝滑的缩略图列表(Thumbnail List)
前端·javascript·vue.js·ecmascript·deepseek
圣心2 小时前
Ollama 快速入门
开发语言·javascript·人工智能
禾苗种树2 小时前
使用echart的dataZoom的labelFormatter自定义时间范围
前端·javascript·vue.js·echart
m0_748240912 小时前
SpringMVC 请求参数接收
前端·javascript·算法
Hermione_log2 小时前
【vue项目如何利用event-stream实现文字流式输出效果】
前端·javascript·vue.js
Little_Code3 小时前
关于uniapp使用renderJS中调用父类方法和参数的使用
前端·javascript·uni-app
qq_316837753 小时前
uniapp h5端和app端 使用 turn.js
开发语言·javascript·uni-app
程序员远仔4 小时前
【Vue.js 和 React.js 的主要区别是什么?】
前端·javascript·css·vue.js·react.js·性能优化·html5