前端实现下载pdf的两种方法

876 阅读1分钟

1、使用html2canvas和jspdf插件实现

该方式是通过html2canvas将HTML页面转换成图片,然后再通过jspdf将图片的base64生成为pdf文件。
实现步骤如下:

(1)下载插件模块:

npm install html2canvas jspdf --save             
npm install jspdf --save

(2)定义功能实现方法
在项目工具方法存放文件夹utils中创建htmlToPdf.js文件,
代码如下:

export default{
  install (Vue, options) {
    Vue.prototype.getPdf = function () {
    //      console.log('begin download...')
      setTimeout(( =>{
        //console.log('downloading...')
      var title = this.htmlTitle
        html2canvas($("#pdfDom")[0],{
        **配置参数**
         width: 980,
         height: 980 * 1.4142 * this.page,
         background: "#fff",
         scale: 2.1,
         tainttest: true,
         logging: false,
         letterRendering: true,
         llowTaint: true
      }).then(function (canvas) {
        var contentWidth = canvas.width
          var contentHeight = canvas.height
          // A4纸宽高
          var A4Width = 595.28
          var A4Height = 841.89
          // 一页pdf显示html页面生成的canvas高度
          var pageHeight = contentWidth / A4Width * A4Height
          // 未生成pdf的html页面高度
          var leftHeight = contentHeight
          // pdf页面偏移
          var position = 0
          // pdf页面内边距
          var pdfPadding = 0
          // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
          var imgWidth = A4Width
          var imgHeight = A4Width / contentWidth * contentHeight
          var pageData = canvas.toDataURL('image/jpeg', 1.0)
          var pdf = new JsPdf('', 'pt', 'a4')
         // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
          // 当内容未超过pdf一页显示的范围,无需分页
          if (leftHeight < pageHeight) {
            pdf.addImage(pageData, 'JPEG', pdfPadding, 0, imgWidth, imgHeight)
          } else {
            while (leftHeight > 0) {
              pdf.addImage(pageData, 'JPEG', pdfPadding, position, imgWidth, imgHeight)
              leftHeight -= pageHeight
              position -= A4Height
              leftHeight > 0 && pdf.addPage()
            }
          }
        PDF.save(title + '.pdf')
      }
      }
       },2000)
    }
  }
}

(3)全局引入实现方法
在项目主文件main.js中引入定义好的实现方法,并注册。

import htmlToPdf from '@/components/utils/htmlToPdf'
// 使用Vue.use()方法就会调用工具方法中的install方法
Vue.use(htmlToPdf)

(4)在相关要导出的页面中,点击时调用绑定在Vue原型上的getPdf方法,传入id即可

//html
 <div id="pdfDom">
   //要下载的HTML页面,页面是由后台返回
  <div v-html="pageData"></div>
</div>
<el-button type="primary" size="small" @click="getPdf('#pdfDom')">点击下载</el-button>

原理差不多,只不过下边的方法更好些,下载的pdf页面多的情况也可以下载出来,用上边的方法,内容多的情况,下载会出现空白页的情况,下边的方法我认为是好于上边的。下边的方法我是通过iframe来实现的 2、使用dom-to-image和jspdf插件实现 实现步骤如下:

(1)下载插件模块:

npm install dom-to-image --save             
npm install jspdf --save

(2、3、4) 同上 为避免下载的不全或者后台数据还没加载完全,就下载pdf ,所以最好在mounted里 通过eventBus,执行完获取数据的方法之后加一个延时器,确保后台返回的数据都已经渲染完,再执行下载的方法,

 async mounted() {
  await this.getData();
      this.$nextTick(() => {
        setTimeout(() => {
          this.$Eventbus.$emit("isCanDownload", { isRendered: true });
        }, 1000);
      });
  },

在预览页的mounted里接收一下,判断条件加好,条件成立,再执行下载的方法

 this.$Eventbus.$on("isCanDownload", (val) => {
      if (val.isRendered && this.isDownload) {
        this.isDownload = false;
        this.download();
      }
    });

 window.addEventListener(
      "message",
      (event) => {
        if (event.data === "下载") {
          this.isDownload = true;
        }
      },
      false
    );

在主页面可以这样来写,代码如下

<div class="custom-pdf" v-if="href">
      <iframe
        :src="href"
        frameborder="0"
        id="custom-pdf"
      ></iframe>
    </div>

然后在下载的方法同上getpdf里边调用方法

this.$nextTick(() => {
     document.getElementById("custom-pdf").onload = () => {
       document
         .getElementById("custom-pdf")
           .contentWindow.postMessage(
               "下载",
                location.origin + "/" + router.href
              );
          };
          window.addEventListener(
            "message",
            (event) => {
              if (event.data === "完成") {
                this.isDownloading = false;
              }
            },
            false
          );
        });