根据公司的实际项目需求,要求实现对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
// 点击分页
上一页
{{currentPage}} / {{pageCount}}
下一页
// 声明变量
data() {
return {
pdfSrc: '', // pdf文件src
pageCount: 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文件显示出来了,目前仅仅成功了一小部分,可以说刚刚把这部分功能最基础的部分弄完,后续还会有控制打印、下载、另存、水印…
mounted() {
// 禁止ctrl+S保存 禁止ctrl+P打印
document.addEventListener(
"keydown",
function (event) {
// 禁止ctrl+s
if (event.ctrlKey === true && event.which === 83) {
// console.log('ctrl+s');
event.preventDefault();
}else if(event.ctrlKey === true && event.which === 80) {
// 禁止ctrl+p
event.preventDefault();
}
},
false
);
}
下载文件
// 下载方法
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 = url
dom.setAttribute('download', this.fileName)
document.body.appendChild(dom)
// 执行下载操作
dom.click()
// 下载完成以后向后台发起接口 传入用户id 后台将该用户的下载次数进行加1
......
},
1.安装依赖
yarn add print-js
2.引入插件
import printJS from 'print-js'
3.使用插件(直接传入文件的路径)
printJS(url)
4.官方文档https://printjs.crabbly.com/
打印
// 打印方法
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
......
})
},
// 调用方法传入的参数是水印内容根据实际需求而定,我这里写的是获取当前用户的中文用户名直接传入方法里即可
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", //字体颜色 默认 #dadbdc
rotate: 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这个插件,总结一下我在开发过程中所遇到过的坑,仅供参考可能解决方法有很多种
分割线分割线,这是一条分割线—分割线分割线,这是一条分割线—分割线分割线,这是一条分割线—分割线分割线,这是一条分割线
注意:代码中需要注释的那一行有的版本路径不一样
有的是:// var PdfjsWorker = require('worker-loader!pdfjs-dist/build/pdf.worker.js');
有的是:// var PdfjsWorker = require('worker-loader!pdfjs-dist/es5/build/pdf.worker.js');
// 处理vue-pdf打包文件404
config.module
.rule('worker')
.test(/\.worker\.js$/)
.use('worker-loader').loader('worker-loader')
.options({
inline: true,
fallback: false,
}).end();
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;
//加载完语言文件后清除缓存
delete require.cache[require.resolve('./buffer-loader!pdfjs-dist-sign/cmaps/'+query.name+'.bcmap')];