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

1,874 阅读4分钟

需求背景

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

具体分析

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

经过上一篇,我们已经能够正确的下载图表生成的PDF文件了,打开后也能正确的预览PDF文件的内容了

但是 依然存在以下问题:

  • 如何不下载PDF到本地磁盘,就能上传???

  • 如何保证PDF多页面不空白,也就是如何保证canvas列表已经全部渲染???

  • 如何保证很多个图表渲染的时候页面不卡顿???

今天这篇我们就针对这其中一些问题进行处理:

先来解决第一个问题:如何不下载PDF到本地磁盘,就能上传???

如何保证不下载就能获取文件呢?

通过查看 jsPDF 文档 我们发现 官方文档讲的并不是很全面,多是一些基本示例。或许是我们有认真在网上找相关文档或文章:

那我们不妨直接去看看源码,源码中肯定有:

我们直接打开仓库下dist 目录

映入眼帘的是众多文件:

有 es, node , umd , polyfills ,min, 等各种文件; 我们也不纠结具体看什么规范的文件了,随便找一个就是:就看第一个文件 jspdf.es.js

经过我们大致的浏览先找到 save 方法, 这方法我们熟啊,前面才用过的

大概在 Line:3843 的位置

      ......
      
      case "save":
        API.save(options.filename);
        break;
        
      .....

嗯, 没错, 很像那么回事 继续跟进API.save 方法 Line:6426 行

/**
   * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf').
   * Uses FileSaver.js-method saveAs.
   *
   * @memberof jsPDF#
   * @name save
   * @function
   * @instance
   * @param  {string} filename The filename including extension.
   * @param  {Object} options An Object with additional options, possible options: 'returnPromise'.
   * @returns {jsPDF|Promise} jsPDF-instance     */

  API.save = function (filename, options) {
    filename = filename || "generated.pdf";
    options = options || {};
    options.returnPromise = options.returnPromise || false;
    if (options.returnPromise === false) {
      saveAs(getBlob(buildDocument()), filename);
      if (typeof saveAs.unload === "function") {
        if (globalObject.setTimeout) {
          setTimeout(saveAs.unload, 911);
        }
      }
      return this;
    } else {
      return new Promise(function (resolve, reject) {
        try {
          var result = saveAs(getBlob(buildDocument()), filename);
          if (typeof saveAs.unload === "function") {
            if (globalObject.setTimeout) {
              setTimeout(saveAs.unload, 911);
            }
          }
          resolve(result);
        } catch (e) {
          reject(e.message);
        }
      });
    }

  }; // applying plugins (more methods) ON TOP of built-in API.

  // this is intentional as we allow plugins to override

  // built-ins

好家伙,他接受一个filenme 和 options 作为参数

  • filename 的缺省值为 generated.pdf
  • options 的值默认为{},支持配置 returnPromise 默认 false 我们发现发调用了saveAs 这个API,在方法的描述中他也说了 Uses FileSaver.js-method saveAs. 这个 saveAs 是 FileSaver.js 中的 我们这就不去细看 saveAs 返回值了

得,来都来了,不去看一眼,也说不过去了,那就看一眼:

FileSaver 中 saveAs 方法并没有返回值;

Line:144

经过以上我们以为可以通过配置 save 方法的 options.returnPromise = true 拿到Bolo 是不可能的了!

但是,但是,但是细心的我们发现,他传入给saveAs 方法的确是 Bolo;

没错就是这一行:

var result = saveAs(getBlob(buildDocument()), filename);

从 getBlob 这个 函数名 为们发现 我们要的就是这个这个:

到现在 摆在眼前已经有两条路了:

第一: 把这文件拷贝不出来,吧 blob 用一个函数 导出来,供我们自己使用;

第二: 继续找看有没有直接提供相应的API:

权衡过后 我们选择先看第二,第一作为最后的倔强

那就继续浏览代码: 仔细的我们发现API这个类肯定是暴露给用户使用的,我们继续找 我们发现了 一个 output 的方法,看这命名 应该是大体不会差了;

找了一会儿 看到了Line 3828 output 的实现

Line:3850

  ....  
  case "blob":
        return getBlob(buildDocument());
  ....

这正是我们寻找的 Blob 对象

那我们知道了 使用output方法 可以拿到了 Blob 对象 剩下的水道渠成了

将 Blob 转 成 file

调用 上传 API 就能直接上传了

参考代码


//Blob 转 file 并上传
function uploadFile(pdfDoc){
    const filename = 'xxx.pdf'
    const blob = pdfDoc.output('blob',{filename})
    const file = new File([blob],filename,{type:blob.type})
    
    const formData = new FormData()
    
    formData.append('file',file)
   
    API.upload(formData).then((res)=>{
        const fileId = res.data 
        // TODO 同步给第三方
       
    })     
}

截止现在 我们已经实现了转pdf并上传文件服务器,将结果同步给第三方的多有操作了

下一篇 我们将讨论用什么方式渲染多张图表不卡顿,目前的v-for 方式会导致页面卡顿或浏览器崩溃 查看下上篇 使用jsPdf 将页面生成PDF文件并自动上传 DEMO(一) 查看下一篇 使用jsPdf 将页面生成PDF文件并自动上传 DEMO(三)

最后的最后

请出今天的主角 小趴菜

小趴菜.jpeg