加载中...

Vue3+pdfjs-dist实现PDF文件展示

Vue3+pdfjs-dist实现PDF文件展示

前言:最近和小伙伴们一起合作完成一个面试推送系统的书写,其中一个功能就是渲染PDF文件,展示用户上传的面经,干了整整一周,终于实现了这个功能,现在将自己实现的过程分享给大家.

1.pdfjs-dist展示PDF文件的原理解释

pdfjs-dist展示pdf文档的原理,实际上是将pdf中的内容渲染到解析,然后渲染到 canvas 中进行展示,因此我们使用pdfjs渲染出来的pdf文件,实际上是一张张canvas图片。

2.安装pdfjs-dist

pdfjs-dist下载官网

New Image

打开VScode,使用ctl+`打开控制台,输入:npm i pdfjs-dist 安装pdfjs-dist

3. 搭建基础的页面代码

话不多说,上代码:

  1. <template>
  2. <div class="interviewVideo_main" id="videoContainer">
  3. <!--此处根据pdf的页数动态生成相应数量的canvas画布-->
  4. <canvas
  5. v-for="pageIndex in pdfPages"
  6. :id="`pdf-canvas-` + pageIndex"
  7. :key="pageIndex"
  8. style="display: block"
  9. ></canvas>
  10. </div>
  11. </template>

4.使用Vue3语法实现PDF文件的多页展示

4.1引入ref、pdfjs-dist
  1. import { ref, reactive, onMounted, nextTick } from "vue";
  2. //注意引入的写法
  3. import * as pdfjsLib from "pdfjs-dist/build/pdf";
4.2将会用到的数据声明为响应式
  1. let pdfDoc = reactive({}); // 保存加载的pdf文件流
  2. let pdfPages = ref(0); // pdf文件的页数
  3. //pdf文件的链接
  4. let pdfUrl = ref("需要展示的文件名");
  5. let pdfScale = ref(1.0); // 缩放比例
4.3定义获取pdf文档流与pdf文件的页数的方法:loadFile
  1. //获取pdf文档流与pdf文件的页数
  2. const loadFile = async (url) => {
  3. //注意我的pdfjs-dist的版本是3.9.179,其他的版本需要更换版本号,不然运行时会报版本不匹配的错
  4. //外部链接引入,存在安全性问题
  5. //pdfjsLib.GlobalWorkerOptions.workerSrc =
  6. //"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.9.179/pdf.worker.min.js";
  7. //内部链接引入
  8. pdfjsLib.GlobalWorkerOptions.workerSrc =
  9. "../../node_modules/pdfjs-dist/build/pdf.worker.min.js";
  10. const loadingTask = pdfjsLib.getDocument(url);
  11. loadingTask.promise.then((pdf) => {
  12. console.log(pdf);
  13. pdfDoc = pdf; //获取pdf文档流
  14. pdfPages.value = pdf.numPages;//获取pdf文件的页数
  15. nextTick(() => {
  16. renderPage(1);
  17. });
  18. });
  19. };
''
运行
4.4定义渲染pdf文件的方法:renderPage
  1. const renderPage = (num) => {
  2. pdfDoc.getPage(num).then((page) => {
  3. const canvasId = "pdf-canvas-" + num;
  4. const canvas = document.getElementById(canvasId);
  5. const ctx = canvas.getContext("2d");
  6. const dpr = window.devicePixelRatio || 1;
  7. const bsr =
  8. ctx.webkitBackingStorePixelRatio ||
  9. ctx.mozBackingStorePixelRatio ||
  10. ctx.msBackingStorePixelRatio ||
  11. ctx.oBackingStorePixelRatio ||
  12. ctx.backingStorePixelRatio ||
  13. 1;
  14. const ratio = dpr / bsr;
  15. const viewport = page.getViewport({ scale: pdfScale.value });
  16. canvas.width = viewport.width * ratio;
  17. canvas.height = viewport.height * ratio;
  18. canvas.style.width = viewport.width + "px";
  19. canvas.style.height = viewport.height + "px";
  20. ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
  21. const renderContext = {
  22. canvasContext: ctx,
  23. viewport: viewport,
  24. };
  25. page.render(renderContext);
  26. if (num < pdfPages.value) {
  27. renderPage(num + 1);
  28. }
  29. });
  30. };
''
运行
4.5在onMounted钩子中调用loadFile方法
  1. //调用loadFile方法
  2. onMounted( () => {
  3. loadFile(pdfUrl.value);
  4. });

5完整代码实现:

  1. <template>
  2. <div class="interviewVideo_main" id="videoContainer">
  3. <!--此处根据pdf的页数动态生成相应数量的canvas画布-->
  4. <canvas
  5. v-for="pageIndex in pdfPages"
  6. :id="`pdf-canvas-` + pageIndex"
  7. :key="pageIndex"
  8. style="display: block"
  9. ></canvas>
  10. </div>
  11. </template>
  12. <script setup>
  13. import { ref } from "vue";
  14. import * as pdfjsLib from "pdfjs-dist/build/pdf";
  15. let pdfDoc = reactive({}); // 保存加载的pdf文件流
  16. let pdfPages = ref(0); // pdf文件的页数
  17. let pdfUrl = ref(""); //pdf文件的链接
  18. let pdfScale = ref(1.0); // 缩放比例
  19. //调用loadFile方法
  20. onMounted(() => {
  21. loadFile(pdfUrl.value);
  22. });
  23. //获取pdf文档流与pdf文件的页数
  24. const loadFile = async (url) => {
  25. pdfjsLib.GlobalWorkerOptions.workerSrc =
  26. "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.9.179/pdf.worker.min.js";
  27. const loadingTask = pdfjsLib.getDocument(url);
  28. loadingTask.promise.then((pdf) => {
  29. console.log(pdf);
  30. pdfDoc = pdf;
  31. pdfPages.value = pdf.numPages;
  32. nextTick(() => {
  33. renderPage(1);
  34. });
  35. });
  36. };
  37. //渲染pdf文件
  38. const renderPage = (num) => {
  39. pdfDoc.getPage(num).then((page) => {
  40. const canvasId = "pdf-canvas-" + num;
  41. const canvas = document.getElementById(canvasId);
  42. const ctx = canvas.getContext("2d");
  43. const dpr = window.devicePixelRatio || 1;
  44. const bsr =
  45. ctx.webkitBackingStorePixelRatio ||
  46. ctx.mozBackingStorePixelRatio ||
  47. ctx.msBackingStorePixelRatio ||
  48. ctx.oBackingStorePixelRatio ||
  49. ctx.backingStorePixelRatio ||
  50. 1;
  51. const ratio = dpr / bsr;
  52. const viewport = page.getViewport({ scale: pdfScale.value });
  53. canvas.width = viewport.width * ratio;
  54. canvas.height = viewport.height * ratio;
  55. canvas.style.width = viewport.width + "px";
  56. canvas.style.height = viewport.height + "px";
  57. ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
  58. const renderContext = {
  59. canvasContext: ctx,
  60. viewport: viewport,
  61. };
  62. page.render(renderContext);
  63. if (num < pdfPages.value) {
  64. renderPage(num + 1);
  65. }
  66. });
  67. };
  68. </script>
  69. <style>
  70. #videoContainer {
  71. height: 842px;
  72. }
  73. </style>

以上就是实现PDF文件多页展示的内容了,如果其他的小伙伴有其他的方法或者思考请批评指正 

6.使用本地文件

如果直接引用本地PDF文件,浏览器会报错:network.js:101 Not allowed to load local resource

原因如下:

浏览器的安全策略禁止直接加载本地文件。这是浏览器的安全限制,旨在防止恶意代码读取用户的本地文件系统。

处理方法:

1.将本地的PDF文件,放在public文件夹下,然后直接引用文件链接即可

New Image

New Image

2.放在assets文件下,使用import引入项目

New Image

我采用的方法是配合后端,将所有的PDF文件存放在阿里云中,借助第三方增加安全性,如果在使用阿里云存放后,报错:

New Image

可以依据阿里云官方提供的配置文件进行配置,链接如下:
阿里云文件跨域配置