vue前端预览pdf并加水印、ofd文件,控制打印、下载、另存,vue-pdf的使用方法以及在开发中所踩过的坑合集

根据公司的实际项目需求,要求实现对pdf和ofd文件的预览,并且需要限制用户是否可以下载、打印、另存pdf、ofd文件,如果该用户可以打印、下载需要控制每个用户的下载次数以及可打印的次数。正常的预览pdf很简单,直接调用浏览器的预览就可以而且功能也比较全,但是一涉及到禁止用户打印、另存的话就不可以用浏览器自带的预览方式了。那就只能寻找插件来模拟了,之前在eletron-vue项目中使用过pdfjs以及vue-pdf插件,效果不是特别好,vue-pdf底层其实也是用的pdfjs的东西,当时在客户端项目中莫名的报一些错误(可能是因为版本问题,当时项目比较着急没有太多时间去排坑)果断弃坑选择了iframe预览pdf文件,也可以达到当时项目需求。但是,目前的需求背景是管理端以及门户端想要实现此功能,脱离了electron的限制,我决定还是使用vue-pdf比较靠谱,我也简单安装并测试使用了一下,没有问题可以正常预览本地文件并且并没有报electron中的不知名的错误。下面先来说一下插件的具体使用方法:

1.安装vue-pdf依赖:

npm/yarn i/add vue-pdf

2.import导入并注册依赖(哪个界面需要在哪个界面单独导入即可)

// 单组件引用
import pdf from 'vue-pdf' 
// 然后在component中进行注册
components: {pdf
},

3.界面中使用vue-pdf

// 点击分页
<div><el-button size="small" type="primary"  @click="changePdfPage(0)" class="turn" :class="{grey: currentPage==1}">上一页</el-button>{{currentPage}} / {{pageCount}}<el-button size="small" type="primary"  @click="changePdfPage(1)" class="turn" :class="{grey: currentPage==pageCount}">下一页</el-button>
</div>
<div class="content" ref="printContent"><pdfid="myIframe"ref="pdfDom":src="pdfSrc" :page="currentPage" @num-pages="pageCount=$event" @page-loaded="currentPage=$event" @loaded="loadPdfHandler"> </pdf>
</div>
// 声明变量
data() {return {pdfSrc: '', // pdf文件srcpageCount: 0, // pdf文件总页数currentPage: 1, // pdf文件页码}
},
// 获取文件流和文件总页数 
async getFileInfo() {let formData = new FormData();formData.append('wjid', this.wjid)formData.append('yhid', this.userId)let res = await getFileBuffer(formData)if(res.status === 200){const blob = new Blob([res.data])let pageRes = await getFilePages(formData)this.pdfSrc = URL.createObjectURL(blob)this.pageCount = pageRes.data.content}else{this.$message({message: res.message,type: 'error'});}
},
// 改变PDF页码,val传过来区分上一页下一页的值,0上一页,1下一页
changePdfPage (val) {if (val === 0 && this.currentPage > 1) {this.currentPage--}if (val === 1 && this.currentPage < this.pageCount) {this.currentPage++}
},// pdf加载时
loadPdfHandler (e) {this.currentPage = 1 // 加载的时候先加载第一页
},

以上代码是从后端获取pdf文件的文件流,await getFileBuffer(formData)请求接口赋值给res,根据res的状态值为200进行判断文件流是否成功返回,返回成功后通过new Blob([res.data]) 转换成blob,然后通过URL.createObjectURL(blob)获取pdfSrc。

await getFilePages(formData)接口是单独获取pdf总页数。

上图为根据后端返回的pdf数据流预览的此pdf文件,现在只是把pdf文件显示出来了,目前仅仅成功了一小部分,可以说刚刚把这部分功能最基础的部分弄完,后续还会有控制打印、下载、另存、水印…

我们可以一个一个来实现,首先说一下禁止浏览器的快捷键ctrl+s(保存)、ctrl+p(打印),可以通过以下代码禁止当前界面这两个快捷键的使用,直接上代码:
mounted() {// 禁止ctrl+S保存   禁止ctrl+P打印document.addEventListener("keydown",function (event) {// 禁止ctrl+sif (event.ctrlKey === true && event.which === 83) {// console.log('ctrl+s');event.preventDefault();}else if(event.ctrlKey === true && event.which === 80) {// 禁止ctrl+pevent.preventDefault();}},false);  
}
文件下载,界面上写好下载按钮,写好click点击事件以及v-if(控制此用户的下载次数,根据用户每次点击下载按钮后台计算下载次数,如果下载次数达到设置的次数,隐藏下载按钮)直接上代码:
<el-button size="small" class="cz-button" type="primary" v-if="downLoadShow" @click="downloadClick">下载文件</el-button>// 下载方法
async downloadClick() {this.fileName = '' // 获取文件名let formData = new FormData();formData.append('wjid', this.wjid)formData.append('yhid', yhid)let res = await getFileBufferDl(formData)// 根据用户id和文件id向后台请求接口返回pdf文件流const blob = new Blob([res])const url = window.URL.createObjectURL(blob) let dom = document.createElement('a')dom.style.display = 'none'dom.href = urldom.setAttribute('download', this.fileName)document.body.appendChild(dom)// 执行下载操作dom.click()// 下载完成以后向后台发起接口 传入用户id 后台将该用户的下载次数进行加1......	
},
文件打印,界面上写好下载按钮,写好click点击事件以及v-if(控制此用户的打印次数,根据用户每次点击打印按钮后台计算打印次数,如果打印次数达到设置的次数,隐藏打印按钮)打印需要下载print-js依赖

1.安装依赖

yarn add print-js

2.引入插件

import printJS from 'print-js'

3.使用插件(直接传入文件的路径)

printJS(url)

4.官方文档https://printjs.crabbly.com/

<el-button size="small" style="margin-right: 90px;" class="cz-button" type="primary" v-if="printShow" @click="billPrintClick">打印</el-button>// 打印方法
billPrintClick() {this.$nextTick(async () => {let formData = new FormData();formData.append('wjid', this.wjid)formData.append('yhid', yhid)let res = await getFileBufferDl(formData)const blob = new Blob([res])const url = window.URL.createObjectURL(blob) //URL.createObjectURL(object)表示生成一个File对象或Blob对象printJS(url)let resName = await getProfile()// 下载完成以后向后台发起接口 传入用户id 后台将该用户的下载次数进行加1......})
},
文件预览水印,正常加水印流程是java后端把文件加上水印后返回给前端,前端可以直接预览、下载、打印带有水印的文件,之前弄过前端加水印方法也比较简单,从网上找了个前端加水印的例子,简单调整了一下,在这也做下记录:
<div class="content" id="myIframe" ref="printContent"><pdf ref="pdfDom" :src="pdfSrc" :page="currentPage" @num-pages="pageCount = $event" @page-loaded="currentPage = $event" @loaded="loadPdfHandler"></pdf>
</div>
// 调用方法传入的参数是水印内容根据实际需求而定,我这里写的是获取当前用户的中文用户名直接传入方法里即可
this.$nextTick(async () => {this.setWatermarkContent(resName.data.userInfo.userName);window.onresize = () => {this.setWatermarkContent(resName.data.userInfo.userName);};
});// 设置水印
setWatermarkContent(resName) {// 创建canvas容器let ele = document.createElement("canvas");ele.width = 250;ele.height = 200;// 水印参数对象let objmsg = {canvas: ele,fontText: resName, // 显示的内容,显示方法传入的内容 字符串格式fontSize: 20, // 水印的字体大小fontFamily: "microsoft yahei", // 字体 fontcolor: "#dadbdc", //字体颜色   默认 #dadbdcrotate: 25, //旋转角度   数字类型textAlign: "left", //水印文字居中方式:left center right  默认 left};this.createWaterMark(objmsg);this.drawWaterMark(ele); // 将水印canvas遮罩层定位到pdf容器中},// 创建canvas水印图片createWaterMark({ canvas, fontText, fontFamily = "microsoft yahei", fontSize = 30, fontcolor = "#dadbdc", rotate = 30, textAlign = "left" }) {let ctx = canvas.getContext("2d");ctx.font = `${fontSize}px ${fontFamily}`;ctx.rotate((-rotate * Math.PI) / 180);ctx.fillStyle = fontcolor;ctx.textAlign = textAlign;ctx.textBaseline = "Middle";ctx.fillText(fontText, canvas.width / 6, canvas.height / 2);},// 给pdf增加水印遮罩层drawWaterMark(ele) {let div = document.createElement("div");div.style.pointerEvents = "none";div.style.position = "absolute";div.style.background = "url(" + ele.toDataURL("image/png") + ") left top repeat";// 获取容器的宽度和高度let width = document.getElementById("myIframe").clientWidth || 700;let height = document.getElementById("myIframe").clientHeight || 700;div.style.width = width + "px";div.style.height = height + "px";document.getElementById("myIframe").appendChild(div);}

上面主要是介绍vue-pdf、printjs插件以及怎么前端控制下载、另存以及打印功能,那么接下来重点说一下vue-pdf这个插件,总结一下我在开发过程中所遇到过的坑,仅供参考可能解决方法有很多种

分割线分割线,这是一条分割线—分割线分割线,这是一条分割线—分割线分割线,这是一条分割线—分割线分割线,这是一条分割线

1. 本地开发时预览一点毛病都没有,就是build打完包后放到服务器上无法预览报错:提示xxx.worker.js 500 或者 404
解决方法:需要改两个地方:
① 修改依赖下的文件位置:node_modules/vue-pdf/src/vuePdfNoSss.vue



注意:代码中需要注释的那一行有的版本路径不一样
有的是:// var PdfjsWorker = require('worker-loader!pdfjs-dist/build/pdf.worker.js');
有的是:// var PdfjsWorker = require('worker-loader!pdfjs-dist/es5/build/pdf.worker.js');

② 找到项目的vue.config.js 文件,在配置文件中新家下面这段代码
    // 处理vue-pdf打包文件404config.module.rule('worker').test(/\.worker\.js$/).use('worker-loader').loader('worker-loader').options({inline: true,fallback: false,}).end();
两个地方改完之后再执行yarn run build打包部署到服务器上发现不会报xxx.worker.js 500 或者 404这种错了,可以正常预览了!

2. 线上环境大部分pdf预览都没有问题,但是有个别pdf文件里面的内容显示不全,缺胳膊少腿,不是这个字没显示出来就是那个字丢了的(本地还是没有问题)
解决方法:在网上找了一下,也有人遇到类似问题,原因是缺少相应字体 ,需要在界面上引入CMapReaderFactory,然后把url传进去生成新的路径赋值给this.pdfSrc,就可以解决
import pdf from 'vue-pdf'
import CMapReaderFactory from 'vue-pdf/src/CMapReaderFactory'let loadingTask = pdf.createLoadingTask({url: URL.createObjectURL(blob),cMapPacked: true,CMapReaderFactory
})
this.pdfSrc = loadingTask;
3. 当我们使用上个问题中的CMapReaderFactory时,会引发另外一个问题,就是预览的时候显示空白的问题,显示不全有空白页,控制台还不报错
我也去网上找了相关问题的解决方法,有人说是因为缓存问题,第二次加载时取的是初次加载PDF文件时的语言文件的loadModules的缓存,但是取的过程中导致了失败,返回了空值,修改的时候只需要加上一段话
//加载完语言文件后清除缓存
delete require.cache[require.resolve('./buffer-loader!pdfjs-dist-sign/cmaps/'+query.name+'.bcmap')];

4. 打包部署到服务器上后,预览时死活看不到内容,控制器不报错,就是空白,本地开发也没问题
这个问题啥招我都试了,我同时做管理端以及门户端,都需要预览pdf,用的都是vue-pdf,代码逻辑一模一样,而且后台接口也一模一样,门户端开发环境和部署环境一点问题都没有,管理端开发环境没问题部署完以后到线上就是不展示内容,这个问题我改了一天,没办法在本地测试,只能改一点发给现场一个包现场部署一版给我测一下好不好用,一天我给现场发包就发了20个,马上下班的时候我给现场发了个包,和现场哥们说这个不行就暂时换回备份的包吧,说实话我对这个包真没报啥希望。竟然解决了,我听到以后都震惊了改了一天给现场发了20个包,最后这个包啥都没改竟然好用了,我也很诧异,最后发现是我把vue-pdf降了几个版本,什么都没改就好使了。之前用的是最新的"vue-pdf": “4.3.0”,我卸了重新按了个4.0.7的竟然解决了!!!
5. Uncaught SyntaxError: Unexpected token ‘<‘
从接触vue-pdf开始,就感觉不是啥善茬,最开始是在electron-vue项目中接触,安装完依赖以后就报Uncaught SyntaxError: Unexpected token ‘<‘这个错,网上找了好多这个问题的解决方法,但是并没有什么用,最后这个问题也没解决,在electron项目中直接弃坑了,换成iframe预览pdf文件,也可以达成想要的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/582535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机操作系统(OS)——P1操作系统概述

1、操作系统的概念(定义) 1.1、什么是操作系统 __操作系统&#xff08;Operating System&#xff0c;OS&#xff09;&#xff1a;__是指控制和管理整个计算机系统的__硬件和软件__资源&#xff0c;并合理的组织调度计算机的工作和资源的分配&#xff1b;以__提供给用户和其它…

Java三层架构/耦合/IOC/DI

一.三层架构 controller/web 控制层。接收前端发送的请求&#xff0c;对请求进行处理&#xff0c;并响应数据。 service 业务逻辑层,处理具体的业务逻辑。 dao 数据访问层(Data Access Object)&#xff0c;也称为持久层。负责数据访问操作&#xff0c;包括数据的增、…

矩阵理论基本知识

1、矩阵范数、算子范数 矩阵无穷范数是非自相容范数&#xff0c;矩阵1-范数、矩阵2-范数是自相容范数矩阵2-范数&#xff1a;Frobenius范数&#xff0c;是向量2-范数的自然推广。 ∥ A ∥ m 2 ∥ A ∥ F ∑ a i j ∗ a i j \|A\|_{m2}\|A\|_{F}\sqrt{\sum a_{ij}^*a_{ij}} ∥…

pytest实现多进程与多线程运行超好用的插件

前言 如果想分布式执行用例&#xff0c;用例设计必须遵循以下原则&#xff1a; 1、用例之间都是独立的&#xff0c; 2、用例a不要去依赖用例b 3、用例执行没先后顺序&#xff0c; 4、随机都能执行每个用例都能独立运行成功每个用例都能重复运行&#xff0c;不影响其它用例 这…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第五节 引用类型复制问题及用克隆接口ICloneable修复

深入浅出图解C#堆与栈 C# Heaping VS Stacking 第五节 引用类型复制问题及用克隆接口ICloneable修复 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节…

九九乘法表c 语言 用于打印九九乘法表

以下是一个简单的C语言程序&#xff0c;用于打印九九乘法表&#xff1a; #include <stdio.h>int main() {int i, j;for (i 1; i < 9; i) {for (j 1; j < i; j) {printf("%d*%d%-2d ", j, i, i*j);}printf("\n");}return 0; }解释&#xff1…

JavaScript练习题第(四)部分

大家好关于JavaScript基础知识点已经发布&#xff1a;需要的大家可以去我的主要查看 &#xff08;当然了有任何不会的&#xff0c;可以私信我&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 为了巩固大家学习知识点给大家准备几道练习题&#xff1a; 当然&…

网络编程『简易TCP网络程序』

&#x1f52d;个人主页&#xff1a; 北 海 &#x1f6dc;所属专栏&#xff1a; Linux学习之旅、神奇的网络世界 &#x1f4bb;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f324;️前言&#x1f326;️正文TCP网络程序1.字符串回响1.1.核心功能1.2.程序…

CGAL的D维包围盒相交计算

包围盒相交测试是一种用于快速判断两个三维对象是否相交的方法&#xff0c;而AABB树则是一种数据结构&#xff0c;常用于加速场景中的射线检测和碰撞检测。 首先&#xff0c;让我们了解一下包围盒相交测试。这种测试的目的是为了快速判断两个三维对象是否相交&#xff0c;而不需…

同化的题解

时间限制: 1000ms 空间限制: 524288kB 题目描述 古人云&#xff1a;“近朱者赤近墨者黑”。这句话是很有道理的。这不鱼大大和一群苦命打工仔被安排进厂拧螺丝了。 进厂第一天&#xff0c;每个人拧螺丝的动力k都是不同且十分高涨的。但是当大家坐在一起后会聊天偷懒&#xf…

【算法刷题】python刷题--合并链表

[23] 合并 K 个升序链表 from typing import List,Optional class ListNode:def __init__(self, val0, nextNone):self.val valself.next next# lc codestart # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # …

在微服务中如何实现全链路的金丝雀发布?

目录 1. 什么金丝雀发布&#xff1f;它有什么用&#xff1f; 2.如何实现全链路的金丝雀发布 2.1 负载均衡模块 2.2 网关模块 2.3 服务模块 2.3.1 注册为灰色服务实例 2.3.2 设置负载均衡器 2.3.3 传递灰度发布标签 2.4 其他代码 2.4.1 其他业务代码 2.4.2 pom.xml 关…

利用C语言实现set——手搓set

在 C 语言中&#xff0c;可以使用数组来手动实现 Set&#xff08;集合&#xff09;的功能。具体来说&#xff0c;以下是一种简单的在 C 语言中手动实现 Set 的方法&#xff1a; 一、定义数组来存储set元素 首先&#xff0c;定义一个数组来存储 Set 的元素。数组中的每个元素代…

CSS 向上扩展动画

上干货 <template><!-- mouseenter"startAnimation" 表示在鼠标进入元素时触发 startAnimation 方法。mouseleave"stopAnimation" 表示在鼠标离开元素时触发 stopAnimation 方法。 --><!-- 容器元素 --><div class"container&q…

SAP VA01 创建带wbs号的销售订单包 CJ067的错误

接口错误提示如下 SAP官方 CJ067 124177 - VA01: CJ067 during WBS acct assgmt with a different business area S4的core 刚好能用上 实施 这个note后成功

引力魔方的操作知识

引力魔方的一些操作以及问题如图所示&#xff1a; 主要包含以下内容&#xff1a; 1.如何搭建计划&#xff1f; 2.创意素材说明&#xff1b; 3.如何出价&#xff1f; 4.如何提高引力魔方点击率&#xff1f; 5.如何优化定向和溢价&#xff1b; 6.冷启动&#xff1a;计划开…

基于WEB的网上购物系统的设计与实现(附:源码 论文 sql文件)

摘 要 随着计算机网络技术的飞速发展和人们生活节奏的不断加快&#xff0c;电子商务技术已经逐渐融入了人们的日常生活当中&#xff0c;网上商城作为电子商务最普遍的一种形式&#xff0c;已被大众逐渐接受。因此开发一个网上商城系统&#xff0c;适合当今形势&#xff0c;更加…

DRF从入门到精通六(排序组件、过滤组件、分页组件、异常处理)

文章目录 一、排序组件继承GenericAPIView使用DRF内置排序组件继承APIView编写排序 二、过滤组件继承GenericAPIView使用DRF内置过滤器实现过滤使用第三方模块django-filter实现and关系的过滤自定制过滤类排序搭配过滤使用 三、分页组件分页器一&#xff1a;Pagination&#xf…

【11】ES6:async/await

一、概念 async/await 是 ES2017&#xff08;ES8&#xff09;的新特性&#xff0c;它是一种基于 Promise 实现的异步编程方式。async/await 也是一种语法糖。 1、async/await 实现了用同步方式来写异步代码&#xff08;promise是链式调用形式写异步代码&#xff09; 2、asyn…

【JavaScript】闭包机制

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…