Vue项目pdf(base64)转图片遇到的问题及解决方法

公司有个业务需求,要求后台传pdf的base64编码给前端,前端显示到界面上,后来在网上搜索了很多关于base64转pdf的文章,都写的不是非常的详细,在实现的过程中遇到很多坑,经过一天的研究终于实现了这个功能,分享一下我在这个功能中遇到的问题和解决方法

要注明的是这里用到的核心插件是pdf.js,原理是动态生成canvas标签,然后通过pdf.js生成一个能渲染出pdf的对象,随后渲染每个canvas,并且生成的pdf是画面的形式,并没有pdf之类的控件

引入插件

这里很多博客都是使用JavaScript原生的方法引入pdf.js,例如使用script标签引入外部的js脚本,或者直接把pdf.js的源码复制到项目中,但是我尝试这些方法的时候都不是特别好用,而且引入后导致项目体积过于庞大,

随后我去github上寻找通过包管理器引入pdf.js的方法,在pdf.js的github上官方说明的是用gulp如何使用pdf.js,但是对于npm来说并没有详细说明,终于我在字里行间发现了这么一句话

To use PDF.js in a web application you can choose to use a pre-built version of the library or to build it from source. We supply pre-built versions for usage with NPM and Bower under the pdfjs-dist name. For more information and examples please refer to the wiki page on this subject.

大致的意思就是如果使用npm包管理器或者bower的话,引入的名字为pdfjs-dist,那么我们使用npm的方法引入这个pdfjs-dist,引入的名字就随意取名了这里我叫PDFJS

 import PDFJS from 'pdfjs-dist'

使用pdfjs-dist

这里后台传给我的是一个由pdf文件名字和pdf的base64编码组成的对象的数组,我取名为pdfDataList

可以看到fileName是pdf的名字,fileVale是pdf文件的base64编码,thumbnail是pdf缩略图的base64编码这里用不到先不管,之前说到需要动态生成canvas节点(这里不会canvas也不要紧,只需要根据代码一步步做就能渲染canvas)

1.首先我们创建一个承载所有canvas节点的父节点,取名为pdfList

2.然后创建一个异步函数showPdf(不懂什么是异步函数的可以去查一下async/await,这里不用异步函数也可以使用promise.then的方法,但是async/await作为异步操作的终极方案最好还是学习一下)

async showPdf() {
  }

使用querySelector选择类名为pdfList的dom节点,随后遍历后台传过来的pdfDataList数组的每一项,这里用到一个浏览器自带的atob()方法解码base64,MDN上是这么解释的:

你可以使用 window.btoa() 方法来编码一个可能在传输过程中出现问题的数据,并且在接受数据之后,使用 atob() 方法再将数据解码。

语法: var decodedData = scope.atob(encodedData);

随后调用pdf.js插件的getDocument方法,getDocument是一个promise,所以使用异步函数的话前面需要加await关键字(不使用异步函数的话在方法后面加.then((pdf)=>{.......}),这个pdf对象和我这个pdf对象是同一个,同时这里暂时也没考虑异步操作出错的情况,有要求的话可以在加个catch捕获错误)

getDocument方法的参数是一个对象,对象键名为data,值为base64解码后的值,此方法返回一个pdf对象,这个对象有几个属性,可以打印出来观察一下

这里我们先用到的是numPages属性,它指的是当前pdf文件有多少页

async showPdf() {
    let pdfList = document.querySelector('.pdfList') //通过querySelector选择DOM节点,使用document.getElementById()也一样
    for(let value of this.pdfDataList){ //遍历后台传过来的pdfDataList
      let base64 = value.fileValue //获得bas464编码
      let decodedBase64 = atob(base64) //使用浏览器自带的方法解码
      let pdf = await PDFJS.getDocument({data: decodedBase64}) //返回一个pdf对象
      let pages = pdf.numPages //声明一个pages变量等于当前pdf文件的页数
    }
  }

获取当前pdf文件的对象和当前pdf文件的所有页数后,循环遍历每个页数,执行如下操作:

1)动态创建canvas节点
2)调用pdf对象原型上的getPage()方法和getViewport()方法,依次传入当前循环的页数和canvas的缩放大小(这里不懂的可以直接复制黏贴)
3)渲染当前的canvas节点
4)调用page对象的render()方法渲染当前页,此方法也是一个promise,需要使用await关键字等到状态为resolve后再执行之后的代码
5)给显示当前页面的canvas节点一个className为canvas方便修改样式,最后把这个canvas节点插入到pdfList节点中

async showPdf() {
    let pdfList = document.querySelector('.pdfList') //通过querySelector选择DOM节点,使用document.getElementById()也一样
    for(let value of this.pdfDataList){ //遍历后台传过来的pdfDataList
      let base64 = value.fileValue //获得bas464编码
      let decodedBase64 = atob(base64) //使用浏览器自带的方法解码
      let pdf = await PDFJS.getDocument({data: decodedBase64}) //返回一个pdf对象
      let pages = pdf.numPages //声明一个pages变量等于当前pdf文件的页数
      for (let i = 1; i <= pages; i++) { //循环页数
       let canvas = document.createElement('canvas')
       let page = await pdf.getPage(i) //调用getPage方法传入当前循环的页数,返回一个page对象
       let scale = 1;//缩放倍数,1表示原始大小
       let viewport = page.getViewport(scale);
       let context = canvas.getContext('2d'); //创建绘制canvas的对象
       canvas.height = viewport.height; //定义canvas高和宽
       canvas.width = viewport.width;
       let renderContext = {
        canvasContext: context,
        viewport: viewport
       };
       await page.render(renderContext)

       canvas.className = 'canvas' //给canvas节点定义一个class名,这里我取名为canvas
       pdfList.appendChild(canvas) //插入到pdfList节点的最后
      }
    }
  }

至此页面上就会多出一个canvas节点并且显示当前pdf文件的第一页的图片,如果当前pdf文件有多页就会渲染出多个canvas节点,有多个pdf文件就会先循环外层,然后再循环内层,把每个pdf文件的每一页都生成一个canvas节点

 

修改样式

渲染出页面后还有个要注意的点,Vue框架会给每个组件的DOM节点生成一个自定义属性,而节点动态生成的canvas节点,并没有data-v-xxxxx这样的自定义属性

而Vue会给每个组件里面的样式添加这个自定义属性,Vue框架这样做可以防止样式的相互污染(也就是style旁边的scoped属性)

我们这里可以在这个style下面再创建一个style写入样式来达到修改canvas样式的效果,但是记得这样做你整个项目里面类名叫canvas的都会获得这个样式,需要注意

写在最后

这里使用的是动态生成canvas节点然后渲染这个节点生成的图片,然而直接使用createElement生成一个节点并且频繁操作DOM会对性能有一定的影响,如果有更好的方法欢迎留言交流,感谢观看。希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • vue导出html、word和pdf的实现代码

    导出的页面组件如下: <template> <div id="resumeId"> <resumeHtml ref="resume" @on-download="download"/> </div> </template> 1.导出html 方法: 1)获取要导出的组件页面的css把它设置成js变量一文本并通过export导出 2)获取要导出组件页面的html的dom标签代码,通过thi

  • vue中如何实现pdf文件预览的方法

    今天产品提出一个优化的需求,就是之前我们做的图片展示就是一个img标签搞定,由于我们做的是海外后台管理系统,那边的人上传的文件时pdf格式,vue本事是不支持这种格式文件展示的,于是就google搜索,发现有iframe.embed.vueshowpdf(测试了不咋好用).pdf等,本文说一下pdf插件的使用过程. 说明:iframe标签这种,对于有的链接是可以的,比如这种链接在服务器端没有设置享有头content-disposition,就可以直接显示,如下: 想复制代码如下: <iframe

  • Vue网页html转换PDF(最低兼容ie10)的思路详解

    HTML转PDF: 1.页面底层实现--Vue:最低兼容ie10 2.实现思路: 1> 使用html2canvas.js将网页转换为图片 2> 使用jsPdf.debug.js将canvas生成的图片转换为pdf文件 具体实现: 要这样实现首先要引入两个插件: html2canvas.js jsPdf.debug.js 注: 因为ie10在canvas截图时候会出现部分样式丢失的情况,所以在代码中有部分修改了页面的样式 所以建议在做截图的时候,将页面代码复制一份到隐藏域,在隐藏域里面做修改,这

  • vue实现word,pdf文件的导出功能

    vue实现word或pdf文档导出的功能,我的项目是:后端返回一个文档流(下图),然后前端对文档流做处理进行下载,代码如下: import axios from 'axios'; axios.get(`url`, { //url: 接口地址 responseType: `arraybuffer` //一定要写 }) .then(res => { if(res.status == 200){ let blob = new Blob([res.data], { type: `application/

  • vue中将网页打印成pdf实例代码

    整理文档,搜刮出一个vue中将网页打印成pdf的代码,稍微整理精简一下做下分享. <template> <div class="pdf-wrap" id="pdfWrap"> <button v-on:click="getPdf">点击下载PDF</button> <div class="pdf-dom" id="pdfDom"></div&

  • vue插件开发之使用pdf.js实现手机端在线预览pdf文档的方法

    目前大多数PC浏览器支持在线预览pdf文件,但大多数手机浏览器还未支持,尝试用手机浏览器打开一个pdf文件会弹出是否下载的提示框.网上查了一些资料,在实现的过程中,还是走了比较多的弯路,最后采用了备受推荐的pdf.js插件来实现. pdf.js可以从github上clone下来,然后本地gulp生成可用的pdf.js和pdf.worker.js(参考readme即可). 不过更简单的方法是使用cnpm来安装: cnpm isntall --save pdfjs-dist,然后可以在项目中使用了,

  • vue2.0全局组件之pdf详解

    目的:像elementUI那样注册全局组件 预览pdf文件 技术支持:使用火狐的pdf.js http://mozilla.github.io/pdf.js/ 准备:新建一个CPdf.vue文件,把火狐demo里面的build里面的pdf.js下载来,并且依赖了elementUI开发的其实就是用了<el-button> 编写: template <template> <div class="cpdf"> <div class="cen

  • Vue导出页面为PDF格式的实现思路

    通过网上的查阅以后,说一下思路.就是将页面转换成图片格式.然后通过图片的base64码.生成PDF..看了他们的文章,做了如下整理.. 说起来很容易,那么具体怎么实现呢? 1 .我们要添加两个模块 第一个.将页面html转换成图片 npm install --save html2canvas 第二个.将图片生成pdf npm install jspdf --save 2.定义全局函数..创建一个htmlToPdf.js文件在指定位置.我个人习惯放在('src/components/utils/h

  • Vue项目pdf(base64)转图片遇到的问题及解决方法

    公司有个业务需求,要求后台传pdf的base64编码给前端,前端显示到界面上,后来在网上搜索了很多关于base64转pdf的文章,都写的不是非常的详细,在实现的过程中遇到很多坑,经过一天的研究终于实现了这个功能,分享一下我在这个功能中遇到的问题和解决方法 要注明的是这里用到的核心插件是pdf.js,原理是动态生成canvas标签,然后通过pdf.js生成一个能渲染出pdf的对象,随后渲染每个canvas,并且生成的pdf是画面的形式,并没有pdf之类的控件 引入插件 这里很多博客都是使用Java

  • Vue项目报错:Uncaught SyntaxError: Unexpected token '<'的解决方法

    目录 问题: 一.public下的index.html没有引入其他外部js文件. 二. public下的index.html引入其他外部js文件. 原因一.引用文件的位置不正确 原因二:配置信息不正确 原因三:script的引入类型不对 总结 问题: 最近做vue项目时,当我访问二级路由的时候,就会报Uncaught SyntaxError: Unexpected token ‘<‘错误,而我访问一级路由不会报错.翻了网上很多资料,都无法解决. 一.public下的index.html没有引入其

  • 详解Vue项目中出现Loading chunk {n} failed问题的解决方法

    最近有个Vue项目中会偶尔出现Loading chunk {n} failed的报错,报错来自于webpack进行code spilt之后某些bundle文件lazy loading失败.但是这个问题的根本原因没有被找到,因为这个问题出现的偶然性太高了,而且有的手机上会出现,有的不会,用模拟器不会出现,用真机又会出现,不知道是网络原因还是webpack的bug.在github.stackoverflow等各种地方也找不到原因和解决方案,这是github上关于这个问题的讨论:Loading chu

  • Vue项目中设置背景图片方法

    在Vue项目开发中我们经常要向页面中添加背景图片,可是当我们在样式中添加了背景图片后,编译打包后,配置到服务器上时,由于路径解析的问题,图片并不能够正确的显示出来,如下CSS样式: background:url("../../assets/head.jpg"); 这个时候我们就要考虑使用其他的方式了,node中提供了一种比较有效的方式来解决这个问题: 1.在data中定义如下: export default { name: 'productdetailspage', data() {

  • 解决vue项目打包后提示图片文件路径错误的问题

    vue项目打包后在production模式下提示图片 '404(not found),在dev环境下好好的,打包了就提示这个错误.这是为什么呢,以前第一次使用vue-cli构建项目的的时候只有两个图片文件,当时部署后报错路径问题, 当时的想法就是简单粗暴,直接在build好的文件修改了文件路径,再后来项目中遇到了一大堆静态资源,修改路径显然是不行的,看了一下生成文件大概知道是文件目录改变了,所以引用的路径也要随之改变,网上最简单的方法是修改 'config/index.js'文件中的 build

  • vue webpack打包后图片路径错误的完美解决方法

    项目用run dev build 打包后,发现很多图片都不显示,在本地是没有问题的啊!找原因发现通过webpack+vuecli默认打包的css.js等资源,路径都是绝对的. 因为我们的图片路径都是经历过文件夹的,在本地引用图片是绝对路径,但打包后因为把配置的static文件夹当成了根路径,所以很多图片找不到都不显示. 解决办法如图: (1). 修改 assetsPublicPath: './' (2).打开webpack.prod.conf.js,在output:增加 publicPath:

  • php实现将base64格式图片保存在指定目录的方法

    本文实例讲述了php实现将base64格式图片保存在指定目录的方法.分享给大家供大家参考,具体如下: <?php header('Content-type:text/html;charset=utf-8'); $base64_image_content = $_POST['imgBase64']; //匹配出图片的格式 if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)){ $ty

  • vue项目每30秒刷新1次接口的实现方法

    在vue.js项目中,经常需要对数据实时更新--每隔xx秒需要刷新一次接口--即需要用到定时器相关原理 我们先看一看2种常用定时器: setInterval(function(){}, milliseconds)--会不停的调用函数 setTimeout(function(){}, milliseconds)--只执行函数一次 乍看之下,setInterval会符合我们的业务需求,然而也需要注意一些坑,单纯的使用setInterval会导致页面卡死!其原因与JS引擎线程有关(有兴趣的童鞋可自行研

  • Vue项目中使用jsonp抓取跨域数据的方法

    下载jsonp npm install jsonp 在js文件夹下新增一个jsonp.js,来封装一个jsonp() 如何封装一个jsonp() 在下载的jsopn中,jsonp(url,options,callback)这个是原生jsonp方法中的参数: 引入下载的jsonp import originJsonp from 'jsonp': 导出自己定义的jsonp函数 //这个jsonp函数是我们自己定义的,与上面的originJsonp不是同一个,originJsonp是一个可以直接引用的

  • vue中使用iview自定义验证关键词输入框问题及解决方法

    一.验证需求 对应配置的关键词输入框,验证要求如下: 1.总字数不能超过7000个: 2.去除配置的关键词特殊符号,得到的关键词组数不能超过300:(如:aaa&(bbb|ccc)|(!ddd|eee)),去掉特殊符号,有5组) 3.单个关键词长度不能超过20:(如:aaaaa&(bbb|ccc)),如果aaaaa长度超过20则提示) 二.解决方法 在关键词输入对应的FormItem中加入一个prop属性,作为验证字段使用:注意该FormItem是包含于Form的: form表单中添加ru

随机推荐