react下实现一个PDF展示组件

8次阅读

共计 4224 个字符,预计需要花费 11 分钟才能阅读完成。

简介:在 react 的 antd-pro 的框架下展示本地的 PDF 文件
效果图:

一、插件选取。
听说过大名鼎鼎的 PDF.js,但是因为是在 react 框架下,所以选取了两个可行的插件

react-pdf
react-pdf-js

两个插件都是对 PDF 进行的封装。两个插件都进行了尝试,相对而言 react-pdf 功能更强大并且文档也比较清晰,但是使用也会相对复杂一点。最后使用的是 react-pdf-js 这个插件。
二、展示选择的文件。
react-pdf-js
第一步:展示一个本地文档。
按照官方的文档:
render() {
let pagination = null;
if (this.state.pages) {
pagination = this.renderPagination(this.state.page, this.state.pages);
}
return (
<div>
<PDF
file=”test.pdf”
onDocumentComplete={this.onDocumentComplete}
page={this.state.page}
/>
{pagination}
</div>
)
}
注意:官方文档没有任何说明。此处的 file 是一个 require 过来的文件。例子:要加载一个 ’E:\1.pdf’, 那么应该那么配置:
const PDFTest = require(‘E:\\1.pdf’);
render() {
let pagination = null;
if (this.state.pages) {
pagination = this.renderPagination(this.state.page, this.state.pages);
}
return (
<div>
<PDF
file={PDFTest}
onDocumentComplete={this.onDocumentComplete}
page={this.state.page}
/>
{pagination}
</div>
)
}
第二步:根据文件选择框更改文件。
这一步被卡住过,刚开始想的是根据选择的文件然后获取文件的实际地址然后运用 require 去获取文件,但是实现的时候发现浏览器的安全策略无法让浏览器获取文件的真实路径。但是!我们可以通过创建一个 URL 对象去获取文件的一个 blob。使用 window.URL.createObjectURL 创建一个 file 文件,并且 react-pdf-js 可以直接接受一个这样子的文件。部分代码如下:
handleButtonOnChange = e =>{
if (e.currentTarget.files.length === 0) return;
const url = window.URL.createObjectURL(e.currentTarget.files[0]);
this.setState({
pdfTest: {
key:url,
file:url,
},
})
}
createPDF = () =>{
const {pageNumber, numPages, pdfTest} = this.state;
if(!pdfTest) return;
return(
<div>
<div className={style.pdfContainer}>
<PDF
key={pdfTest.key}
file={pdfTest.file}
onDocumentComplete={this.onDocumentComplete}
page={pageNumber}
className={style.pdfView}
width=’300px’
/>
</div>
<p style={{float:’right’}}> 第 {pageNumber} 页 共 {numPages} 页 </p>
</div>
)
}
render() {
return (
<div id=’PDFViewer’>
<input id=’id’ type=”file” style={{width:’200px’,height:’35px’}} accept=”.pdf” onChange={this.handleButtonOnChange} />
{this.createPDF()}
</div>
);
}
此处还有一个坑,就是 key 这个值。在文档中没有提到这个值,并且在源代码中也没有怎么出现这个值。这个 key 值应该是标识每个文件的一个唯一标识,当 key 值不同的时候会重新渲染 canvas。
以下做法不推荐:在之前我没发现这个之前,通过修改源码的这个地方改为:
componentWillReceiveProps(newProps) {
const {
page,
scale,
file:oldfile,
onDocumentComplete,
cMapUrl,
cMapPacked,
} = this.props;
const {pdf} = this.state;
const {file:newfile} = newProps;
if(newfile !== oldfile){
PdfJsLib.GlobalWorkerOptions.workerSrc = ‘//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.js’;
PdfJsLib.getDocument({url: newfile, cMapUrl, cMapPacked}).then((newPdf) => {
this.setState({pdf:newPdf});
if (onDocumentComplete) {
onDocumentComplete(newPdf._pdfInfo.numPages); // eslint-disable-line
}
pdf.getPage(page).then(p => this.drawPDF(p));
});
}else{
if (newProps.page !== page) {
pdf.getPage(newProps.page).then(p => this.drawPDF(p));
}
if (newProps.scale !== scale) {
pdf.getPage(newProps.page).then(p => this.drawPDF(p));
}
}
}
手动实现了这个功能,但是这个判定方法存在一些问题,只有再加入一个变量去判断才会完善,就完全和他的 key 这个值一样,但是知道 key 值之后就没有再对源码进行修改了。
第三笔:其他功能。
翻页以及跳页:
handleTurnPage = e =>{
const {pageNumber, numPages, turnPageNumber} = this.state;
if(!numPages){
message.warning(‘ 请先选择 PDF 文件 ’);
return;
}
let newPageNumber = pageNumber;
switch (e.target.id) {
case ‘pageUp’:
newPageNumber -= 1;
if(newPageNumber <= 0){
message.warning(‘ 已经是第一页 ’);
return;
}
break;
case ‘pageDown’:
newPageNumber += 1;
if(newPageNumber > numPages){
message.warning(‘ 已经是最后一页 ’);
return;
}
break;
case ‘numberPage’:
if(!turnPageNumber){
message.warning(‘ 请先输入数字 ’);
return;
}else if(turnPageNumber <= 0||turnPageNumber > numPages){
message.warning(‘ 请输入在页面范围内的数字 ’);
return;
}
newPageNumber = turnPageNumber;
break;
default:
break;
}
this.setState({
pageNumber:newPageNumber,
})
}
render() {
return (
<div id=’PDFViewer’>
<input id=’id’ type=”file” style={{width:’200px’,height:’35px’}} accept=”.pdf” onChange={this.handleButtonOnChange} />
<div style={{float:’right’}}>
<InputNumber onChange={this.onPageNumberInputChange} style={{width:’150px’}} placeholder=’ 输入需要跳转的页 ’ />
<Button onClick={this.handleTurnPage} id=”numberPage”> 确认跳转 </Button>
<Button onClick={this.handleTurnPage} id=”pageUp”> 上一页 </Button>
<Button onClick={this.handleTurnPage} id=”pageDown”> 下一页 </Button>
</div>
{this.createPDF()}
</div>
);
}
完整代码:GitHub。
react-pdf
这个插件的功能很强大,但是使用就相对而言比较复杂。官方的复杂 demo

由于最后使用的是另外的一个插件。这里就只写一下踩坑记录。
1、file 参数。
在这里 file 这个参数和 react-pdf-js 的不一样,在 require 的时候都是一样的,但是在转换的时候不用创建 URL 对象,直接将 input 里面的 file 传过去即可。并且不需要 key。例子:
handleButtonOnChange = e =>{
if (e.currentTarget.files.length === 0) return;
this.setState({
pdfTest: {
file:e.currentTarget.files[0],
},
})
}
2、不显示 text layers
PDF 存在一个问题无法选择里面的文字以及链接,但是 PDF.js 通过在里面添加一层文本层用于辅助选取,但是在这个插件里面会存在一个重影,导致文字显示效果不佳,如图:

在官方文档中提到使用 SVG 可以解决这个问题,但是 SVG 选择出来是乱码。所以在使用的时候希望屏蔽掉 text layer,在文档中也有提到,在 page 中设置。
以上是所有内容。

正文完
 0