blog icon indicating copy to clipboard operation
blog copied to clipboard

前端预览 PDF 的实现方式

Open Runtu4378 opened this issue 7 years ago • 1 comments

结论 1.PDF 的预览行为是浏览器插件(浏览器厂商实现的)所实现的,浏览器对 Content-Typeapplication/pdf 的 GET 请求的默认行为是下载文件 2.要实现可靠的,表现一致的网页端对 PDF 预览,只能使用 js 插件实现

后端提供PDF内容的方式

前端一般是通过调用后端接口去获取 PDF 文件(无论是静态 PDF 文件或是动态的 PDF 文件),不论请求类型,后端返回 PDF 内容通常有以下两种内容格式:

  • 二进制: 此时响应的类型 Content-Typeapplication/pdf ,浏览器对此类型的响应的默认处理方式为下载,在使用了预览插件的浏览器会不下载文件而是触发预览行为
  • Base64: 此时响应的类型可能是 text/html,因为此时的 Base64 编码是放在返回体中的,Base64的处理方式实际是后台将 PDF 文件的二进制内容转换为 Base64 编码,此时的文件预览和上述二进制的处理方式基本是一样的,区别在于你需要告诉浏览器这串字符的格式是 Base64,需要将其转换为 PDF,在字符前面增加这串字符即可 data:application/pdf;base64,

利用浏览器自带插件实现 PDF 预览

缺点

  • 不是所有浏览器都有实现 PDF 文件预览,没有实现预览的浏览器会触发 PDF 文件的下载行为
  • 浏览器实现预览的效果各不相同,而且和浏览器的版本有关(可能有些版本的预览功能有 bug)

优点

  • 成本低,适合作为渐进增强
  • 简单,不依赖于前端

如何开启或关闭浏览器的 PDF 预览功能

比如在 Chrome 浏览器,可以在下列菜单设置 PDF 预览功能的开关

设置 - 高级 - 内容设置 -PDF文档

实现示例

在新窗口打开 PDF 并进行预览或下载

<a href="/helloWorld.pdf" target="_blank">新窗口打开 PDF</a>

在页面内嵌 iframe 中打开 PDF 或进行下载

<iframe
  src="/helloWorld.pdf"
  width="100%"
  height="500px"
>
     
  This browser does not support PDFs. Please download the PDF to view it: <a href="/helloWorld.pdf">Download PDF</a>

</iframe>

在页面内嵌 iframe 中预览 Base64 编码格式的 PDF


<iframe
  src="data:application/pdf;base64,other base64 code"
  width="100%"
  height="500px"
>
        
  This browser does not support PDFs. Please download the PDF to view it: <a href="data:application/pdf;base64,other base64 code">Download PDF</a>
            
</iframe>

注意事项


利用 PDF.js 插件实现 PDF 预览

PDF.js 是 mozilla 开源的 JS 库,大概实现原理是利用算法将 PDF 转换为 js 数据结构然后利用 canvas 在网页中复现

缺点

  • 不支持低版本IE
  • 体积约 3m (gzip前)

优点

  • mozilla 出品,现在仍在更新
  • 不依赖浏览器的PDF预览插件,大概实现原理是利用算法将 pdf 转换为数据结构,然后在 canvas 上输出
  • 提供若干 API 接口,可以自行封装换页等插件

实现示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>editor</title>
  <script src="/static/pdfjs/pdf.js"></script>
</head>

<body>
  <canvas id="the-canvas"></canvas>
</body>

</html>
const pdfjsLib = window['pdfjs-dist/build/pdf']
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/pdfjs/pdf.worker.js'

function initPDF () {
  cosnt url = '/helloWorld.pdf'  // pdf 文件的地址
  const loadingTask = pdfjsLib.getDocument({
    url: url,
  }) // 创建加载文件对象
  loadingTask.promise.then(function (pdf) {
    // pdf 对象是文件加载完成后转换得来的,保存了文件内容以及相关的信息,如 pdf.numPages 能获取到 pdf 的页数
    const num = 0  // 渲染第一页
    pdf.getPage(num).then(function(page) {
      console.log(`Page ${num} loaded`);
      
      var scale = 1.5 // 放大倍数
      var viewport = page.getViewport(scale);
      const canvas = document.getElementById('the-canvas')
      const ctx = canvas.getContext('2d')
      // Prepare canvas using PDF page dimensions
      canvas.height = viewport.height;
      canvas.width = viewport.width;

      // Render PDF page into canvas context
      var renderContext = {
        canvasContext: ctx,
        viewport: viewport,
      }
      var renderTask = page.render(renderContext);  // 生成 canvas 渲染任务
      renderTask.then(function () {
        console.log('Page rendered')  // 渲染完毕
      });
    });
  }).catch(function (reason) {
    console.error('Error: ' + reason)
  });
}

更多示例请见 mozilla/PDF.js 示例


参考资料

Runtu4378 avatar Jul 27 '18 01:07 Runtu4378

How to import Mozilla PDF.js in Vue project? vue + pdfjs-dist 实现pdf流文件在线预览 (解决了Error : Module parse failed: Unexpected token )

https://stackoverflow.com/questions/65750584/how-to-import-mozilla-pdf-js-in-vue-project

PLQin avatar Jul 14 '21 04:07 PLQin