11

我遵循了一个关于如何使用 React 实现 Mozilla 的 PDF 查看器的快速教程。我在这里做了一个codesanbox 。我想知道这是否可以通过导入pdfjs的节点模块来实现。因此,不要将包下载到公用文件夹以将其与导入一起使用:

export default class PDFJs {
  init = (source, element) => {
    const iframe = document.createElement("iframe");

    iframe.src = `/pdfjs-2.5.207-dist/web/viewer.html?file=${source}`;
    iframe.width = "100%";
    iframe.height = "100%";

    element.appendChild(iframe);
  };
}

此外,当 PDF 的源是 URL 时,这种设置不起作用。如果我这样做,我会收到一个错误:

PDF.js v2.5.207 (build: 0974d6052) 消息:文件来源与查看者的不匹配

我已经注释掉了在pdfjs-2.5.207-dist/web/viewer.js中检查文件来源的代码部分:

  //if (origin !== viewerOrigin && protocol !== "blob:") {
  //  throw new Error("file origin does not match viewer's");
  //} 

但是,然后我得到一个错误:

PDF.js v2.5.207 (build: 0974d6052) 消息:获取失败

我怎样才能解决这个问题?是否可以像模块一样将这个包导入到反应组件中,我如何将它用于带有 URL 的外部资源的 PDF?

4

4 回答 4

2

这是一个带有 Mozilla 的查看器和您的 pdf的工作代码框。

注意事项:

  1. 您的 pdf 必须通过 HTTPS 提供,否则您会收到此错误:

混合内容:“https://codesandbox.io/”上的页面是通过 HTTPS 加载的,但请求了不安全的资源“http://www.africau.edu/images/default/sample.pdf”。此请求已被阻止;内容必须通过 HTTPS 提供。

  1. 托管 pdf 的服务器应允许您的应用程序域使用Access-Control-Allow-Origin,或位于同一来源,否则您会收到此错误:

CORS 策略已阻止从源“https://lchyv.csb.app”访问“https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf”获取:否请求的资源上存在“Access-Control-Allow-Origin”标头。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。

  1. 出于演示目的,我使用了https://cors-anywhere.herokuapp.com/<URL_TO_PDF>,它Access-Control-Allow-Origin: *为您设置,但不应在生产中使用!

所以总而言之,由于浏览器的限制,您的 pdf 没有加载。直接在您的应用程序中导入pdfjs,并从头开始构建查看器(这是很多工作),不会解决这些问题。

于 2021-01-23T02:20:03.657 回答
2

推荐人政策:strict-origin-when-cross-origin / 使用外部来源

pdf 应位于同一主机上(包括相同的协议)。将 pdf 托管在与您的应用程序/网站相同的 url 上,应该可以解决这个问题。

允许在其他页面中加载 pdf 可能会导致各种安全风险。

如果您想在自己的主页上显示外部 pdf 的最新版本,基本上有两种选择。

在您的服务器上托管 PDF

运行一个服务器脚本 (cron),它会下载 pdf 并将其托管在您自己的服务器上。

允许跨域

如果您有权访问托管 pdf 的服务器,则可以发送标头以允许跨域。

Access-Control-Allow-Origin: *

如何将 pdfjs 与 yarn/npm 一起使用

这方面的文档真的很糟糕,但是他们有一个存储库pdfjs-dist和一些相关的文档。

安装

npm install pdfjs-dist

用法(来自DOC

import * as pdfjsLib from 'pdfjs-dist';
var url = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf';

// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';

// Asynchronous download of PDF
var loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then(function(pdf) {
  console.log('PDF loaded');
  
  // Fetch the first page
  var pageNumber = 1;
  pdf.getPage(pageNumber).then(function(page) {
    console.log('Page loaded');
    
    var scale = 1.5;
    var viewport = page.getViewport({scale: scale});

    // Prepare canvas using PDF page dimensions
    var canvas = document.getElementById('the-canvas');
    var context = canvas.getContext('2d');
    canvas.height = viewport.height;
    canvas.width = viewport.width;

    // Render PDF page into canvas context
    var renderContext = {
      canvasContext: context,
      viewport: viewport
    };
    var renderTask = page.render(renderContext);
    renderTask.promise.then(function () {
      console.log('Page rendered');
    });
  });
}, function (reason) {
  // PDF loading error
  console.error(reason);
});

服务工作者

确实需要 service worker - 没有它 pdfjs 就不能工作,所以 reactpdf 也不能。

如果您使用 CRA,并且不想使用 CDN,您可以执行以下步骤:

1)将工作人员复制到公用文件夹

cp ./node_modules/pdfjs-dist/build/pdf.worker.js public/scripts

2)注册Service Worker

pdfjsLib.GlobalWorkerOptions.workerSrc = `${process.env.PUBLIC_URL}/scripts/pdf.worker.js`
于 2021-01-21T09:22:06.287 回答
0

注意:正如其他人已经说过的那样,仅使用 react(或任何客户端库),如果不解决 CORS 问题,就无法获取外部资源(在您的情况下为 PDF)。您需要某种服务器端技术来解决它。(除非您拥有/有权访问外部资源服务器)


查看您提供的沙箱代码,您似乎已经在使用 node js,但该解决方案适用于所有人。

基本上,您会请求服务器为您获取文件,然后将文件作为响应负载返回。 例如,节点服务器监听请求fetchPdf并将文件本身作为响应返回

app.post('/fetchPdf', asyncMiddleware(async (req, res, next) => {
    const pdfPath = await downloadFile(req.body.url);
    if (pdfPath) {
        res.type('application/pdf');
        res.sendFile(pdfPath);
        res.on('finish', function () {
            try {
                fs.unlinkSync(pdfPath);
            } catch (e) {
                console.error(e);
                console.log(`Unable to delete file ${pdfPath}`);
            }
        });
    } else
        res.status(404).send('Not found');
}));

function downloadFile(url) {
    return new Promise((resolve, reject) => {
        const absoluteFilePath = path.join(__dirname, `public/${crypto.randomBytes(20).toString('hex')}.pdf`);
        const file = fs.createWriteStream(absoluteFilePath);
        console.log(`Requested url ${url}`);
        const request = http.get(url, function (downloadResponse) {
            downloadResponse.pipe(file).on('finish', () => {
                resolve(absoluteFilePath);
            });
        }).on('error', function (err) {
            fs.unlink(absoluteFilePath);
            resolve(null);
        });
    });
}

注意:出于教育和学习目的,这将起作用,但是以这种方式将代码部署到生产环境存在各种安全问题。

首先,您的服务器应该能够向Internet 上的任何站点发出请求
其次,如果没有某种身份验证,您的站点将成为任何希望下载被 CORS 阻止的外部资源的人的热点(类似于 [https://cors -anywhere.herokuapp.com])


至于你的第二个问题,是的,可以将 pdfjs 库与 react & npm 一起使用。
您可以参考yurydelendik 的repo,取自官方pdf.js mozilla 存储库。
我还创建了一个与上述服务器端解决方案相同的分支。

于 2021-01-26T11:22:36.783 回答
0

我对您的示例进行了更改,因此它将接受一个 URL

我的代码如下

import pdfjsWorker from "pdfjs-dist/build/pdf.worker.entry";
const pdfjsLib = import("pdfjs-dist/build/pdf");

export default class PDFJs {
  init = (source, element) => {
    pdfjsLib.then((pdfjs) => {
      pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
      var loadingTask = pdfjs.getDocument(`${source}`);
      loadingTask.promise.then((pdf) => {
        pdf.getPage(1).then((page) => {
          var scale = 1.5;
          var viewport = page.getViewport({ scale: scale });
          var canvas = document.createElement("canvas");
          var context = canvas.getContext("2d");
          canvas.height = viewport.height;
          canvas.width = viewport.width;
          element.appendChild(canvas);
          var renderContext = {
            canvasContext: context,
            viewport: viewport
          };
          page.render(renderContext);
        });
      });
    });
  };
}

你可以在这里看到结果

于 2021-01-20T22:16:41.757 回答