react下實現一個PDF展示組件

簡介:在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中設置。

以上是所有內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章