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;以__提供给用户和其它…

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…

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

目录 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 关…

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…

【JavaScript】闭包机制

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

java设计模式学习之【解释器模式】

文章目录 引言解释器模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用表达式解析示例代码地址 引言 在我们的日常生活中&#xff0c;语言的翻译和理解是沟通的关键。每种语言都有自己的语法规则&#xff0c;而翻译人员和计算机程序需要理解并遵循这些规则来…

手写Promise

目录 前言 状态 使用方法 构造函数 对象 结论 第一阶段 搭建基本结构 思路 代码实现 测试一下 实现then方法获取异步值 思路 改造代码如下 测试一下 成功场景 失败场景 实现类的resolve,reject以及catch方法 思路 代码实现 测试一下 第一阶段总结 第二阶…

《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识(11)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识&#xff08;10&#xff09; 1.3 PCI总线的存储器读写总线事务 1.3.2 Posted和Non-Posted传送方式 PCI总线规定了两类数据传送方式&#xff0c;分别是Posted和Non-Posted数据传送…

关于Redis面试题

前言 之前为了准备面试&#xff0c;收集整理了一些面试题。 本篇文章更新时间2023年12月27日。 最新的内容可以看我的原文&#xff1a;https://www.yuque.com/wfzx/ninzck/cbf0cxkrr6s1kniv Redis 是什么 全名&#xff1a;远程字典服务。这是一个开源的在内存中的数据结构存…

硅像素传感器文献调研(二)

写在前面&#xff1a;从上篇文章的参考文献中看到一篇文献&#xff0c;现在也精读一下&#xff0c;今天还有一个任务是把上篇文献整体脉络用流程图的形式完整梳理一下。 哈哈哈哈哈哈&#xff1a;代表没太搞明白的部分 如何写论文&#xff1a; 引言部分&#xff1a;基础理论…