使用jsPdf 将页面生成PDF文件并自动上传 DEMO(一)

1,471 阅读5分钟

需求背景

在项目中有这样一个需求,需要调用其他系统业务API生成页面(canvas)渲染图表,并将图表导出成PDF文件,上传到服务器,并将文件地址,传给第三方业务系统,使第三方业务系统能够通过文件服务器下载该图表PDF文件

以上就是原始的需求描述了

当我们接到这个需求的时候就已经大概或许猜测出,这事流程较复杂,能不能直接甩锅到后端同学,让后端同学去完成这一系列的骚操作。

尽管 前后端 都是可以完成的。

但是后端有一个难点(耗时点)是画图表的操作;只要图表能够画出来,剩下的无非就是 canvas 转 pdf 文件,然后调用文件服务 service 进行上传,同步第三方系统,完事。

呐. 需要前端提供 画图表的模版什么的,或者需要前端吧canvas 转 图片 或者 pdf .

这一波操作下来, 好像前端的工作量也没怎么减少。

经过一系列沟通与讨论,发现一个很nice点就是,生成图表的业务,已经封装成npm 包了。

那这事 就显得 不那么棘手了。

现在 我们具体简单整理 整个流程:

step1. 在本系统安装 图表 npm 包,调用业务系统API 生成图表

step2. 将生成的canvas图表 转成 pdf

step3. 将pdf上传拿到文件路径

step4. 将pdf文件路径同步给第三方

整个需求梳理下来的流程就大概就是这了。

具体分析

经过上述的需求分析 我们大致 明确了每一步需要做的操作了。

现在我们就具体分析一下可能存在的细节或遗漏点

先查看了 图表的npm 包,先集成 这个包 进入 我们自己的业务系统,并隐藏

同时生成多张图表,并将多张生成一个PDF文件 ???

PDF的分页问题?

PDF的临时存储 ? 要不要先下载下来 让用户再手动选择文件进行上传?能不能接受这多一步的操作,或者我们能不能规避这一步的操作呢?

PDF的上传? 到底是用户手动选择上传 还是 程序自动静默上传 ?其实我们都偏向后者,减少用户操作,一步到位

因为是多张图表,具体多少张不知道,可能几张,可能几时上百张,没有给出具体阈值? 这个问题就显得比较尖锐了,主要反应在两个方面:

第一方面:一次渲染这么多canvas 页面必定会卡顿或直接浏览器崩溃

第二方面:就算上面说的页面不卡顿或者浏览器不崩溃,生成的pdf文件大小如果太大,上传也将是一个问题,这个时候就又要考虑大文件上传方案了。

第三方面:这一系列的操作虽然是自动静默进行的,能不能在页面上反馈出进度或状态,让用户知晓当前进行到哪一步了。

那我们还是本着先实现功能,再优化细节的方式进行了demo的编写;

本文就仅给出大概实现思路和部分参考代码:

参考实现

集成 图表 组件 comA


<button @click="handleCanvasToPdf">下载PDF</button>

<div ref="canvasList">
    <com-a v-for="(item,index) in list" :key="item.id" :ref="'comA_'+index" />
<div>   

安装 jsPDF

npm install jspdf -S

....
methods:{
    
    async handleLoadData(){
       const res = await API.request('/xxx/xxx',{size:3})
       const list = res.data || [],
    },
    
    
    handleCanvasToPdf(){
        const canvasList = this.$refs.canvasList.querySelectorAll('canvas')
        if(!canvasList.length){ return }
        const cacheImageList = canvasList.map((canvas)=>{
            return canvas.toDataURL('image/jpeg',1)
        })
       
       // 创建一个pdf 文档 指定 现实方式,单位,及默认大小
       const pdf = new jsPDF('p','pt','a4')
       
       const width = pdf.internal.pageSize.getWidth()
       const height = pdf.internam.pageSize.getHeight()
       
       // 把图片内容添分页加到 pdf 文档中,这里没有做计算分页,我们一张图片就渲染一页
       cacheImageList.forEach((img,index)=>{
           pdf.addImage(img,'JPEG',0,0,width,height)
           // 分页 , 如果不是最后一页就添加一页
           if(imageCacheList.length - 1 !== index){
               padf.addPage()
           }
       })
       
       // pdf 文档已经准备好了,现在只需要导出就好了
       pdf.save('temp.pdf')
        
    }

}
....
运行上面的方法 就能自动下载pdf 文件了
打开下载下来的PDF文件,果然如预期,直呼内行 .......

当调整参数 size = 20 后等请求完成后,立马点击 下载PDF 新的问题就来了:

PDF中 部分 页面内容空白,而且是靠后,越往后越空白!甚至在前几页内容都不完整,有些图表内容也仅仅只渲染了一半;

只需搭眼一看,就知道是渲染时间的问题,所有的canvas 还没有渲染完成就被拿去转换成文件了。要如何解决这个问题呢 ? 请看下一篇!

最后的最后

经过我们简单的demo 发现整个过程还是很顺畅的 现在所暴露的问题也慢慢浮现了,让我们简单整理下一现在要解决的问题:

  1. 如何不下载PDF到本地磁盘,就能上传???
  2. 如何保证PDF多页面不空白,也就是如何保证canvas列表已经全部渲染???
  3. 如何保证很多个图表渲染的时候页面不卡顿???

下一篇 我们继续分析如何解决上述问题

使用jsPdf 将页面生成PDF文件并自动上传 DEMO(二)

下面有请今天的主角小趴菜:

小趴菜.jpeg