Pdf.js和viewer.js。将流或Blob传递给viewer。

28

我在寻找解决方案时遇到了麻烦: 我使用Javascript从SQL文件流字段中检索PDF blob的方法如下(这是一个Lightswitch项目)

var blob = new Blob([screen.WebReportsPdfFilesStream.selectedItem.Pdf], { type: "application/pdf;base64" });

我已经拥有这个blob,甚至可以将它转换为文件流或base64格式("JVBERi0....." 或 "%PDF 1.6 ......", 等等)。

到目前为止还没有问题。

现在我需要在一个浏览器中显示它。我更喜欢在一个新窗口中打开查看器,但我也可以嵌入到我的页面中。

我想知道是否可以直接将blob或流传递给查看器并显示文档。我尝试了一些东西,例如

PDFView.open(pdfAsArray, 0)

在这种情况下,嵌入式浏览器中没有任何变化发生。

pdfAsArray很好,因为我可以将其流附加到页面内的画布上显示。 我只想显示查看器,而不是将PDF嵌入画布中,可能是在新窗口中。

有人能提供一些用JavaScript实现此目标所需的代码吗?

5个回答

28

我正在使用PDFJS.version = '1.0.1040'; PDFJS.build = '997096f';

以下是适用于我获取base64格式PDF数据的代码:

function (base64Data) {
  var pdfData = base64ToUint8Array(base64Data);
  PDFJS.getDocument(pdfData).then(function (pdf) {
    pdf.getPage(1).then(function (page) {
      var scale = 1;
      var viewport = page.getViewport(scale);
      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');
      canvas.height = viewport.height;
      canvas.width = viewport.width;
      page.render({ canvasContext: context, viewport: viewport });
    });
  });

  function base64ToUint8Array(base64) {
    var raw = atob(base64);
    var uint8Array = new Uint8Array(raw.length);
    for (var i = 0; i < raw.length; i++) {
      uint8Array[i] = raw.charCodeAt(i);
    }
    return uint8Array;
  }
}

这个函数可以作为一个 API 调用 Promise 的成功函数。我在这里将 PDF 渲染到画布元素 myCanvas 上。

<canvas id="myCanvas"></canvas>

这显示了pdf的第一页,但没有功能。我可以理解为什么需要viewer。如果我将其连接到viewer.html / viewer.js,我会编辑我的回答。

编辑:如何连接viewer

1bower.json中添加"pdfjs-viewer": "1.0.1040"

2 Html:

<iframe id="pdfViewer" src="lib/pdfjs-viewer/web/viewer.html" style="width: 100%; height: 700px;" allowfullscreen="" webkitallowfullscreen=""></iframe>

3 修改viewer.js文件中的愚蠢默认文档:

var DEFAULT_URL = '';

4 控制器:

var pdfjsframe = document.getElementById('pdfViewer');
pdfjsframe.onload = function() {
  LoadPdfDocument();
};

$scope.myApiCallThatReturnsBase64PdfData.then(
  function(base64Data) {
    $scope.base64Data = base64Data;
    LoadPdfDocument();
  },
  function(failure) {

    //NotificationService.error(failure.Message);

  });

function LoadPdfDocument() {
  if ($scope.PdfDocumentLoaded)
    return;
  if (!$scope.base64Data)
    return;

  var pdfData = base64ToUint8Array($scope.base64Data);
  pdfjsframe.contentWindow.PDFViewerApplication.open(pdfData);

  $scope.PdfDocumentLoaded = true;
}

function base64ToUint8Array(base64) {
  var raw = atob(base64);
  var uint8Array = new Uint8Array(raw.length);
  for (var i = 0; i < raw.length; i++) {
    uint8Array[i] = raw.charCodeAt(i);
  }
  return uint8Array;
}

1
@QuentinJanon,抱歉,$scope是来自AngularJS的。你需要改变的是无论何时看到$scope,都要将其变成一个变量。不是函数内部的局部变量,而是在任何函数外部定义一个变量。 - toddmo
3
将近一年后,你的回答拯救了我的生命,谢谢你!=) - Gustavo Gabriel
2
救命稻草。这个答案应该被包含在pdf.js文档中,从未见过如此糟糕的文档。不管怎样,谢谢!!! - fgonzalez
1
@Matt,在Chrome调试器中在那一行加一个断点,看看哪些内容没有定义。pdfjsframe?contentWindow?PDFViewerApplication?通常情况下,数据是在文档加载后才出现的。让我知道,我们会从那里开始。 - toddmo
1
@toddmo,非常抱歉。您的代码是正确的。我漏掉了 onload。因此,PDFViewerApplication 未定义。 - Matt
显示剩余15条评论

14
如果您有一个类型化数组(例如Uint8Array),则可以使用PDFView.open(typedarray,0); 打开文件。

如果您有一个BlobFile对象,则必须将数据转换为类型化数组才能查看它:

var fr = new FileReader();
fr.onload = function() {
    var arraybuffer = this.result;
    var uint8array = new Uint8Array(arraybuffer);
    PDFView.open(uint8array, 0);
};   
fr.readAsArrayBuffer(blob);

另一种方法是为Blob/File对象创建一个URL:
var url = URL.createObjectURL(blob);
PDFView.open(url, 0);

如果PDF查看器托管在与嵌入框架相同的上,则可以通过将Blob URL传递给查看器来查看PDF:

var url = URL.createObjectURL(blob);
var viewerUrl = 'web/viewer.html?file=' + encodeURIComponent(url);
// TODO: Load the PDF.js viewer in a frame or new tab/window.

谢谢Rob。 我知道前两个部分,甚至知道可以使用PDFView.load函数打开pdf.js返回的pdfDocument。 我不知道最后一段代码var url = URL.createObjectURL(blob); var viewerUrl = 'web/viewer.html?file=' + encodeURIComponent(url); // TODO: Load the PDF.js viewer in a frame or new tab/window.我认为这将帮助我找到正确的位置,因为我的首选是在新窗口中打开查看器,而不是在嵌入式查看器中加载文件。 - Efrael
所以,这应该可以工作: var url = URL.createObjectURL(blob); var viewerUrl = 'Scripts/pdfViewer/web/viewer.html?file=' + encodeURIComponent(url); window.open(viewerUrl, "_blank"); - Efrael

12

我终于让PDFView.open方法起作用了。现在,如果我将查看器嵌入我的页面并像Rob在前两个示例中建议的那样调用打开函数,它就可以工作。

对于那些正在寻找此类解决方案的人,我在这里提供一些代码:

这是我的Lightswitch mainPage.lsml.js中的代码。 js脚本(pdf.js、viewer和Others)在Lightswitch项目的主html页面(Default.html)中引用;我认为它应该可以在任何其他基于html而不是Lightswitch的页面上工作。

myapp.MainPage.ShowPdf_execute = function (screen) {
// Write code here.
// Getting the stream from sql
var blob = new Blob([screen.WebReportsPdfFilesStream.selectedItem.Pdf], { type: "application/pdf;base64" });
// Pass the stream to an aspx page that makes some manipulations and returns a response
var formData = new FormData();
formData.tagName = pdfName;
formData.append(pdfName, blob);
var xhr = new XMLHttpRequest();
var url = "../OpenPdf.aspx";
xhr.open('POST', url, false);
xhr.onload = function (e) {
    var response = e.target.response;
        var pdfAsArray = convertDataURIToBinary("data:application/pdf;base64, " + response);
        var pdfDocument;
// Use PDFJS to render a pdfDocument from pdf array
    PDFJS.getDocument(pdfAsArray).then(function (pdf) {
        pdfDocument = pdf;
        var url = URL.createObjectURL(blob);
        PDFView.load(pdfDocument, 1.5)
    })
};
xhr.send(formData);  // multipart/form-data 
};

这是convertDataURIToBinary函数

function convertDataURIToBinary(dataURI) {
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));

for (i = 0; i < rawLength; i++) {
    array[i] = raw.charCodeAt(i);
}
return array;
}
仍然缺少的是直接将流传递到viewer.html页面的可能性,以便在新窗口中打开并具有单独的ui来进行渲染。由于我得到了一个内部没有文档的空查看器,因此这段代码仍然无法工作。
var blob = new Blob([screen.WebReportsPdfFilesStream.selectedItem.Pdf], { type: "application/pdf;base64" });
var url = URL.createObjectURL(blob);
var viewerUrl = 'Scripts/pdfViewer/web/viewer.html?file=' + encodeURIComponent(url);
window.open(viewerUrl);

看起来encodeURIComponent(url)没有将一个好的对象传递给查看器以加载到查看器中。 有任何想法或建议吗?

看起来encodeURIComponent(url)没有为查看器提供正确的对象进行加载。 有什么想法或建议吗?

救命稻草。这解决了我的问题,应该被接受为正确答案。 - lux
嗨@Efrael。你的解决方案真的很酷,但我不确定BASE64_MARKER是什么?在哪里可以获取它?或者我需要在那个变量中放什么,请描述一下? :) - Oleg Klimenko
1
只是一个字符串: var BASE64_MARKER = ';base64, '; - Efrael

5
var url = URL.createObjectURL(blob);

var viewerUrl = 'web/viewer.html?file=' + encodeURIComponent(url);

// TODO: Load the PDF.js viewer in a frame or new tab/window.

//-- Abobe code opens a new window but with errors : 'Missing PDF 
// blob://http://server-addr:port/converted-blob' 

1
我正在使用iframe中的viewer;
 <iframe
    id="pdfIframe"
    src="pdfjs/web/viewer.html"
    style="width: 100%; height: 100%;"
  >
  </iframe>

使用Fetch API的方式如下:

 fetch(pdfSourceUrl).then((response: Response) => {
            response.blob().then((blob) => {
              var url = URL.createObjectURL(blob);
              pdfIframe.src = `pdfjs/web/viewer.html?file=${url}`;
            });
          });

最终创建的iframe src如下所示;

http://localhost:9000/pdfjs/web/viewer.html?file=blob:http://localhost:9000/14f6a2ec-ad25-40ab-9db8-560c15e90f6e


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接