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

效果图:

clipboard.png

一、插件选取。

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

两个插件都是对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通过在里面添加一层文本层用于辅助选取,但是在这个插件里面会存在一个重影,导致文字显示效果不佳,如图:

clipboard.png

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

以上是所有内容。


Bonny
117 声望6 粉丝

无他,唯手熟尔。