Pdf.js body.getReader 报错问题

标题也可以改成:pdf.js 使用 fetch, axios 来渲染获取渲染内容

一、问题背景描述

在日常工作中,使用 pdf.js 做 文件的预览,是一个很常见的需求!

而且这个组件非常强大,背后的维护组织是:mozilla,火狐浏览器的组织,所以非常的强大!

这里放一段 pdf.js 官方 github 仓库的示例代码:

const pdfPath = "../learning/helloworld.pdf";

// Setting worker path to worker bundle.
pdfjsLib.GlobalWorkerOptions.workerSrc =
  "../../build/browserify/pdf.worker.bundle.js";

// Loading a document.
const loadingTask = pdfjsLib.getDocument(pdfPath);
loadingTask.promise
  .then(function (pdfDocument) {
    // Request a first page
    return pdfDocument.getPage(1).then(function (pdfPage) {
      // Display page on the existing canvas with 100% scale.
      const viewport = pdfPage.getViewport({ scale: 1.0 });
      const canvas = document.getElementById("theCanvas");
      canvas.width = viewport.width;
      canvas.height = viewport.height;
      const ctx = canvas.getContext("2d");
      const renderTask = pdfPage.render({
        canvasContext: ctx,
        viewport,
      });
      return renderTask.promise;
    });
  })
  .catch(function (reason) {
    console.error("Error: " + reason);
  });

代码不多,使用方式也是很简单:

  • 把 pdf.js 引进来;
  • 设置一下worker,在后台解析 pdf;
  • 然后就是加载一个 PDF 文档;
  • 然后就是面向 pdf.js 提供的 API 编程,把内容通过 canvas 渲染展示给用户(我们平时应该更关心的是这个步骤,中途还会做很多屏幕,缩放的适配,懒加载,内存的回收 …)

一切都很美好,直到遇到:手机QQ,QQ浏览器

本来在其他地方已经调试的好好的东西,在这两个平台上莫名其妙的挂了,还报了一个错误。大致是这样的:
在这里插入图片描述

贴一个文字版的:

n { message: “undefined is not an object (evaluating ‘e.body.getReader’)”, name: “UnknownErrorException”, details: “TypeError: undefined is not an object (evaluating ‘e.body.getReader’)” }

二、问题定位

是不是看着很迷惑,很少见过这种报错对吧!

这个是从 pdf.js 里面报出来的,其实跟业务代码没有关系。那这个时候你可能就想了,既然不是我们的代码,我们能通过自己的努力解决吗?

答案是可以的,不然也不会有这个文章!

需要解决这个问题,那就得先了解为什么会出现这个问题。

刚刚也说过,我们在其他浏览器是没有问题的,怎么突然换到 QQ 上就不行了呢?一个念头闪过:兼容性

这个问题必须是兼容性问题。那在定位一下,到底是哪个API出了问题呢?

说实话,这个就不好确定了。这里其实找了很久也没有确定,就只能跟着报错的信息一步一步找!直接把 pdf.js 的源码 clone 下来,自己在源码里面看!

那既然报错是:TypeError: undefined is not an object (evaluating ‘e.body.getReader’)

大概就能猜出来:body.getReader 应该是这里出问题了,肯定有一个是 undefined

那就到源代码里面找:
在这里插入图片描述

这里可以大概解释一下,这里主要是 pdf.js 加载文件的地方,可以看到其实两个报错的位置都是在加载文件!

所以聪明的你,一下应该就能猜到答案,兼容性问题就出在这里吧 - Fetch + ReadableStream

这里主要就是使用了 Fetch 加载文档,然后 pdf.js 从接口中流式读取内容!

如果不是很熟悉这几个API的可以看看:

反复尝试,确定问题就是出现在这里!

三、解决方案

问题找到了,但是我们可以改变框架的代码吗?

答案是不能的,但是我们可以给初始化的逻辑改了。

getDocument 入口函数我们可以看到:
在这里插入图片描述

pdf.js 是支持我们传递一个 pdf 的二进制数据作为初始化的!

我们可以从这里入手。而且也分析过,我们报错的是因为加载 pdf 文件导致的。所以只要在入口控制住,不让 pdf.js 走 URL 的方式初始化那就不会出问题!

解决思路就是:我们可以通过 fetch 手动去请求 pdf 文件内容,然后通过 ArrayBuffer 的方式再塞给 pdf.js

这里就是八仙过海各显神通了!大家可以自己行实现。我把自己的做法贴出来,仅供参考!

import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf';

let pdfDoc;
try {
  pdfDoc = await pdfjsLib.getDocument(url).promise;
} catch (error) {
  if (String(error).indexOf('response.body.getReader') > -1) {
    // 某些浏览器内核不支持 Fetch - response.body.getReader 的模式,专门在这里修复
    const pdfData = await fetch(
      new URL(url, window.location).href,
    );
    const arrayBufferPdf = await pdfData.arrayBuffer(); // 转成 ArrayBuffer 塞给 pdf.js
    pdfDoc = await pdfLib.getDocument({ data: arrayBufferPdf }).promise;
  }
}

// pdf render ...

经过以上的代码,我们在获取PDF内上,就不会有报错问题的存在了,后面的渲染逻辑跟以前是一样的,就不再次赘述!

四、思维拓展

经过了这一番折腾,回过头来看,其实解决问题的方式很简单,如果能早一点想到就好了。

在这里,也把自己想到的一个场景记录下来:展示内存中的PDF文件

大概的意思就是:如果我们想让用户随便选择一个 pdf 文件,我们给他显示出来。如果你看懂了上面的解决方案,那这个需求就很容易实现:

  • 声明一个 input 框,类型是 file
  • 在 onchang 的时候,如果有内容,就 new FileReader() 初始化一个 Reader,读取文件内容
  • Reader 返回的是一个二进制的内容,我们在转一下,变成 Uint8Array 再塞给 pdf.js 就解决问题了!

原文地址:www.abners.cn/pdf-error

大家感兴趣可以看公众号:
在这里插入图片描述

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值