前言:最近和小伙伴们一起合作完成一个面试推送系统的书写,其中一个功能就是渲染PDF文件,展示用户上传的面经,干了整整一周,终于实现了这个功能,现在将自己实现的过程分享给大家.
1.pdfjs-dist展示PDF文件的原理解释
pdfjs-dist展示pdf文档的原理,实际上是将pdf中的内容渲染到解析,然后渲染到 canvas
中进行展示,因此我们使用pdfjs渲染出来的pdf文件,实际上是一张张canvas图片。
2.安装pdfjs-dist
打开VScode,使用ctl+`打开控制台,输入:npm i pdfjs-dist 安装pdfjs-dist
3. 搭建基础的页面代码
话不多说,上代码:
<template>
<div class="interviewVideo_main" id="videoContainer">
<!--此处根据pdf的页数动态生成相应数量的canvas画布-->
<canvas
v-for="pageIndex in pdfPages"
:id="`pdf-canvas-` + pageIndex"
:key="pageIndex"
style="display: block"
></canvas>
</div>
</template>
4.使用Vue3语法实现PDF文件的多页展示
4.1引入ref、pdfjs-dist
import { ref, reactive, onMounted, nextTick } from "vue";
//注意引入的写法
import * as pdfjsLib from "pdfjs-dist/build/pdf";
4.2将会用到的数据声明为响应式
let pdfDoc = reactive({}); // 保存加载的pdf文件流
let pdfPages = ref(0); // pdf文件的页数
//pdf文件的链接
let pdfUrl = ref("需要展示的文件名");
let pdfScale = ref(1.0); // 缩放比例
4.3定义获取pdf文档流与pdf文件的页数的方法:loadFile
//获取pdf文档流与pdf文件的页数
const loadFile = async (url) => {
//注意我的pdfjs-dist的版本是3.9.179,其他的版本需要更换版本号,不然运行时会报版本不匹配的错
//外部链接引入,存在安全性问题
//pdfjsLib.GlobalWorkerOptions.workerSrc =
//"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.9.179/pdf.worker.min.js";
//内部链接引入
pdfjsLib.GlobalWorkerOptions.workerSrc =
"../../node_modules/pdfjs-dist/build/pdf.worker.min.js";
const loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then((pdf) => {
console.log(pdf);
pdfDoc = pdf; //获取pdf文档流
pdfPages.value = pdf.numPages;//获取pdf文件的页数
nextTick(() => {
renderPage(1);
});
});
};
4.4定义渲染pdf文件的方法:renderPage
const renderPage = (num) => {
pdfDoc.getPage(num).then((page) => {
const canvasId = "pdf-canvas-" + num;
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext("2d");
const dpr = window.devicePixelRatio || 1;
const bsr =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1;
const ratio = dpr / bsr;
const viewport = page.getViewport({ scale: pdfScale.value });
canvas.width = viewport.width * ratio;
canvas.height = viewport.height * ratio;
canvas.style.width = viewport.width + "px";
canvas.style.height = viewport.height + "px";
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
const renderContext = {
canvasContext: ctx,
viewport: viewport,
};
page.render(renderContext);
if (num < pdfPages.value) {
renderPage(num + 1);
}
});
};
4.5在onMounted钩子中调用loadFile方法
//调用loadFile方法
onMounted( () => {
loadFile(pdfUrl.value);
});
5完整代码实现:
<template>
<div class="interviewVideo_main" id="videoContainer">
<!--此处根据pdf的页数动态生成相应数量的canvas画布-->
<canvas
v-for="pageIndex in pdfPages"
:id="`pdf-canvas-` + pageIndex"
:key="pageIndex"
style="display: block"
></canvas>
</div>
</template>
<script setup>
import { ref } from "vue";
import * as pdfjsLib from "pdfjs-dist/build/pdf";
let pdfDoc = reactive({}); // 保存加载的pdf文件流
let pdfPages = ref(0); // pdf文件的页数
let pdfUrl = ref(""); //pdf文件的链接
let pdfScale = ref(1.0); // 缩放比例
//调用loadFile方法
onMounted(() => {
loadFile(pdfUrl.value);
});
//获取pdf文档流与pdf文件的页数
const loadFile = async (url) => {
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.9.179/pdf.worker.min.js";
const loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then((pdf) => {
console.log(pdf);
pdfDoc = pdf;
pdfPages.value = pdf.numPages;
nextTick(() => {
renderPage(1);
});
});
};
//渲染pdf文件
const renderPage = (num) => {
pdfDoc.getPage(num).then((page) => {
const canvasId = "pdf-canvas-" + num;
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext("2d");
const dpr = window.devicePixelRatio || 1;
const bsr =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1;
const ratio = dpr / bsr;
const viewport = page.getViewport({ scale: pdfScale.value });
canvas.width = viewport.width * ratio;
canvas.height = viewport.height * ratio;
canvas.style.width = viewport.width + "px";
canvas.style.height = viewport.height + "px";
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
const renderContext = {
canvasContext: ctx,
viewport: viewport,
};
page.render(renderContext);
if (num < pdfPages.value) {
renderPage(num + 1);
}
});
};
</script>
<style>
#videoContainer {
height: 842px;
}
</style>
以上就是实现PDF文件多页展示的内容了,如果其他的小伙伴有其他的方法或者思考请批评指正
6.使用本地文件
如果直接引用本地PDF文件,浏览器会报错:network.js:101 Not allowed to load local resource
原因如下:
浏览器的安全策略禁止直接加载本地文件。这是浏览器的安全限制,旨在防止恶意代码读取用户的本地文件系统。
处理方法:
1.将本地的PDF文件,放在public文件夹下,然后直接引用文件链接即可
2.放在assets文件下,使用import引入项目
我采用的方法是配合后端,将所有的PDF文件存放在阿里云中,借助第三方增加安全性,如果在使用阿里云存放后,报错:
可以依据阿里云官方提供的配置文件进行配置,链接如下:
阿里云文件跨域配置