簡體   English   中英

Mozilla PDF - 如何在反應應用程序中從 url 查看 PDF?

[英]Mozilla PDF - how to view PDFs from url in react app?

我遵循了一個關於如何使用 React 實現 Mozilla 的 PDF 查看器的快速教程。 在這里做了一個密碼箱。 我想知道這是否可以通過導入 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(內部版本: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(內部版本:0974d6052)消息:無法獲取

我怎樣才能解決這個問題? 是否可以將這個包像模塊一樣導入到 react 組件中,我如何將它用於帶有 URL 的外部資源的 PDF?

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

pdf 應位於同一主機上(包括相同的協議)。 將 pdf 托管在與您的應用程序/網站相同的 url 上,應該可以解決這個問題。

允許將 pdf 加載到其他頁面可能會導致各種安全風險。

如果您想在自己的主頁上顯示外部 pdf 的最新版本,基本上有兩種選擇。

在您的服務器上托管 PDF

運行服務器腳本 (cron),該腳本下載 pdf 並將其托管在您自己的服務器上。

允許跨域

如果您有權訪問托管 pdf 的服務器,您可以發送標題以允許跨域。

Access-Control-Allow-Origin: *

如何在yarn/npm中使用pdfjs

這方面的文檔真的很糟糕,但他們有一個存儲庫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)將worker復制到公用文件夾

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`

這是一個帶有 Mozilla 查看器和您的 pdf 的有效代碼

注意事項:

  1. 您的 pdf 必須通過 HTTPS 提供,否則您會收到此錯誤:

混合內容:“https://codesandbox.io/”頁面已通過 HTTPS 加載,但請求了不安全的資源“http://www.africau.edu/images/default/sample.pdf”。 此請求已被阻止; 內容必須通過 HTTPS 提供。

  1. 托管 pdf 的服務器應該允許您的應用程序域使用Access-Control-Allow-Origin ,或者在同一來源,否則您會收到此錯誤:

從源“https://lchyv.csb.app”訪問“https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf”已被 CORS 政策阻止:否請求的資源上存在“Access-Control-Allow-Origin”標頭。 如果不透明響應滿足您的需求,請將請求的模式設置為“no-cors”以在禁用 CORS 的情況下獲取資源。

  1. 出於演示目的,我使用了https://cors-anywhere.herokuapp.com/<URL_TO_PDF> ,它為您設置了Access-Control-Allow-Origin: * ,但不應在生產中使用!

所以總而言之,由於瀏覽器的限制,您的 pdf 沒有加載。 直接在您的應用程序中導入pdfjs並從頭開始構建查看器(這是一項大量工作),並不能解決這些問題。

我對您的示例進行了更改,以便它接受一個 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);
        });
      });
    });
  };
}

你可以在這里看到結果

注意:正如其他人已經說過的,僅使用 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 存儲庫。
我還在此處創建了一個相同的分支,演示了上述服務器端解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM