工作学习总结-pdf.js的踩坑和运用
周末了,终于有这么一整段的时间去总结整理了,这个月很忙,需求很多,年龄不小了,生活中个人的事情也不少。时间是最公平的,因为每个人每天都是过这么长时间,好好珍惜每一分。好了,不感慨了。进入正题吧。
先说一下项目结构:.0 + .0 这个月做了一个pdf展示的需求,后台小伙伴传过来一个pdf格式的url,
这个url长这样:
url = ”https://sg.ibs.baidu.com.cn/download/fla-ybkj-dmz-dev-pri/24a1bed5f3fc46d582d6ff7747445b14?attname=BS012LYT.pdf"
其实就是一个pdf格式的文件的url地址,然后前端展示一下。原本以为实现会很简单,没想坑是一个接一个,下面来一一说一下吧。
1.先把后台传过来的url做一下转化,在中的转化如下:
html的代码 ts中的代码this.url = this.sanitizer.bypassSecurityTrustResourceUrl("assets/viewer/web/viewer.html?file=" + encodeURIComponent(res.data.url));
只有转化之后的url加载到中才有效
2.这个pdf.js是一个泛称,装上之后,会有这么多文件,如图:
html页面中写法如下:
其中
属性规定是否在 中显示滚动条。有三个值 yes 、no、 auto
属性规定 看上去像是包含文档的一部分。
是监听ion- 内容的滚动事件。然后就开始在里面增加滚动后的触发事件了,代码如下:
scrollEvent(event) {this.show = true;let het1 = document.body.clientHeight;let het2 = document.getElementById("deatilShow").clientHeight; this.ngzone.run(() => {let top = event.scrollTop; if (het2 <= het1 + top) {this.buttonShow = false;}})}
上面代码中的this..run()的用法请见我的另外一篇文章工作学习总结–中的的简单使用
当我滚动页面的时候,事件却没有触发,因为我滚动的是页面,也就是嵌入的另外一个页面,所以不会触发事件 。所以我找了一个折中的方案,给加了一个-,当滚动到底部的时候,再往上滑一点,就会触发这个事件了,也就是ion-这个页面滚动了,但是效果不太好,在ios和安卓上体验都不是很好,所以最后也没有采用这种方法。那怎么办呢?
经过自己的搜索+思索和其他小伙伴的帮助,最后还是以一种笨方法解决了。
3.下面描述我探索的过程(探索方法一):
我首先获取这个元素,监听它的滚动事件,获取到的是一个html,如图:
所以给它加滚动事件无效,然后又百度方法,还是没找到有效的方法,最后谷歌了一下,找到了一个方法,就是监听irame中的id为的元素的滚动事件才行,这个id为的元素其实就是.html中的一个元素,所有pdf文件页面都加载到它里面,它在.html中的代码如下:
然后就去获取这个元素
setTimeout(() => {if(this.elementRef.nativeElement.querySelector('.scroll-content #deatilShow') && this.elementRef.nativeElement.querySelector('.scroll-content #deatilShow').contentDocument && this.elementRef.nativeElement.querySelector('.scroll-content #deatilShow').contentDocument.getElementById("viewerContainer")) {clearInterval(this.timer)viewerContainer = this.elementRef.nativeElement.querySelector('.scroll-content #deatilShow').contentDocument.getElementById("viewerContainer");viewerContainer.addEventListener('scroll',(event) => {let scrollTop = event.target.scrollTop ;let clientHeight = event.target.clientHeight ;let scrollHeight = event.target.scrollHeight ;if(scrollTop+clientHeight == scrollHeight){// 添加滚到底部要处理的事件}});}},2000)
为什么加个定时器呢?是因为网速不好的时候,pdf加载很慢,有可能页面还没加载完全,可能这个时候获取不到元素。这个时候在浏览器上跑的时候报跨域的问题,所以在谷歌浏览器设置了一下跨域。这个时候页面上事件都是可以的,都可以触发了。然后打包在ios和安卓手机上测的时候无效,一点效果没有,这个时候就纳闷了。为什么呢?手机连着电脑调试了一下,发现还是这个元素没有获取到。查了一下,猜测应该是pdf.js安全策略,不让外界获取这个元素,因为pdf文件都加载到这个元素下面,怕外界对pdf文件进行修改操作。那电脑上为什么可以呢,也许是因为给谷歌设置了跨域,可以忽略这个风险,具体原因还需后续再仔细去查一下。这个方法又被pass掉了,继续想办法吧
4.探索方法二
获取不到这个元素就没有办法监听它的滚动事件。那既然外界获取不到,我就在.html中获取这个元素,并给它监听这个滚动事件。先试一下,试试又不花钱。
于是我在.html中给它加了这样一段代码
然后在页面上监听发出的事件就可以了
ngOnInit() {window.addEventListener("message",(event)=>{event.data.toBottom?this.show = true:"";event.data.scrolled?this.button = true:"";})}
问题总算解决了,但是这也只是一个临时的方案,后期如果有什么更好的方法还要去优化的。
这个方法中有两个问题需要注意一下,一个是在.html页面中用 ..发出消息,另外就是在父页面进行监听的时候,用event.data的数据来区分监听是哪种消息。
5.最后说一下我对pdfjs这块的源码做了哪些改动
第一点:我注释掉了跨域这一块,在.js里面(在1751行到1753行)
try {var viewerOrigin = new _pdfjsLib.URL(window.location.href).origin || 'null';if (HOSTED_VIEWER_ORIGINS.includes(viewerOrigin)) {return;}var _ref8 = new _pdfjsLib.URL(file, window.location.href),origin = _ref8.origin,protocol = _ref8.protocol;// if (origin !== viewerOrigin && protocol !== 'blob:') {// throw new Error('file origin does not match viewer\'s');// }} catch (ex) {
第二点:需求需要,需要合同文件上展示签章,也就是盖过的章,所以在pdf.work.js里面注释掉这样几行代码
大约注释了四行代码(在28684行到28688行)
if (!Number.isInteger(data.fieldFlags) || data.fieldFlags < 0) {data.fieldFlags = 0;}data.readOnly = _this2.hasFieldFlag(_util.AnnotationFieldFlag.READONLY);// if (data.fieldType === 'Sig') {// data.fieldValue = null;// _this2.setFlags(_util.AnnotationFlag.HIDDEN);// }return _this2;}
我使用的pdf.js是2.0的版本
第三点:清除pdf缓存 为什么说清除pdf缓存呢?例如 我打开一个pdf文件,滑到第6页,然后退出这个页面,再进来还是在第6页。那有没有方法可以让文件从第1页开始加载呢。就开始了我的探索之路,其实开发经验丰富的人,找问题很快也很准,也许是因为开发的需求多了,接触的问题也多了。我先去在插件里面去找,在.js里面找。缓存就是意味着历史记录,于是我先查,我发现了有这样一行代码:
localStorage.setItem('pdfjs.history', databaseStr);
我感觉这就是我要找的,于是我就去浏览器的里面找,果然,请看图:
所以我在.js中的第13457行把它注释掉了
_regenerator.default.mark(function _callee() {var databaseStr;return _regenerator.default.wrap(function _callee$(_context) {while (1) {switch (_context.prev = _context.next) {case 0:databaseStr = JSON.stringify(this.database);// localStorage.setItem('pdfjs.history', databaseStr); case 2:case "end":return _context.stop();}}}, _callee, this);}));
然后就没有缓存了,问题也解决了。
还有一种笨方法就是在pdf的url后面加一个时间戳或者随机数。
以后关于pdf.js遇到的问题,到时候还会补充。