首页 > 其他分享 >前端vue的JsPDF html2canvas 生成pdf并以文件流形式上传到后端(转载)

前端vue的JsPDF html2canvas 生成pdf并以文件流形式上传到后端(转载)

时间:2023-02-19 15:14:34浏览次数:57  
标签:JsPDF 文件 vue res html2canvas code let pdf data

原文地址

1.首先在文件内引入htmlToPdf.js
这里代码引入了html2canvas和jspdf
//需要 npm i html2Canvas 和 npm i jspdf

在这里将getPdf 这个函数挂载到Vue的原型上,最后return一个promise对象(包含了resolve的base64Pdf,以便于处理),在局部组件内可进行.then以进行上传后端等操作。

 

插件代码如下

import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
export default {
  install(Vue, options) {
    /**
     * 
     * @param {*} reportName 下载时候的标题
     * @param {*} isDownload  是否下载默认为下载,传false不下载
     */
    Vue.prototype.getPdf = function (reportName, isDownload = true) {
      //     var target = document.getElementsByClassName("right-aside")[0];
      // target.style.background = "#FFFFFF";
      return new Promise((resolve, reject) => {
        var title = reportName;
        html2Canvas(document.querySelector('#pdfDom'), {
          allowTaint: true
        }).then((canvas) => {
          let contentWidth = canvas.width
          let contentHeight = canvas.height
          //一页pdf显示html页面生成的canvas高度;
          let pageHeight = contentWidth / 592.28 * 841.89
          //未生成pdf的html页面高度
          let leftHeight = contentHeight
          //页面偏移
          let position = 0
          //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
          let imgWidth = 595.28
          let imgHeight = 592.28 / contentWidth * contentHeight
          let pageData = canvas.toDataURL('image/jpeg', 1.0)
          let PDF = new JsPDF('', 'pt', 'a4')
          //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
          //当内容未超过pdf一页显示的范围,无需分页
          if (leftHeight < pageHeight) {
            PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
          } else {
            while (leftHeight > 0) {
              PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
              leftHeight -= pageHeight
              position -= 841.89
              //避免添加空白页
              if (leftHeight > 0) {
                PDF.addPage()
              }
            }
          }
          if (isDownload) {
            PDF.save(title + '.pdf')
          }
          // 删除本地存储的base64字段
          var pdfData = PDF.output('datauristring')//获取base64Pdf
          resolve(pdfData)
        }
        )
      })
    }
  }
}

接下在main.js直接引入刚刚的代码文件

import htmlToPdf from '@/utils/htmlToPdf'

在局部组件时,准备下载时

<button @click="toGetPdf(0)">下载PDF</button>//这种情况是只下载,不上传后端
<button @click="toGetPdf(1, 0)">下载PDF</button>//这种情况是只走上传后端接口,不下载
 toGetPdf(val = false, download = true) {
      /**
     * val 决定走不走上传接口,默认为不上传给后端
     * download 默认是下载
     * /
 
      /* */
      this.$nextTick(() => {
        setTimeout(() => {
          window.scrollTo(0, 0);     //这行代码很重要,它让页面的滚动条跳到了最上方如果点击打印按钮的时候,滚动条没有在最上方,打印内容会是不完整的,体验也会差
          let title ="个人报告"
          this.getPdf(title, download) //download:false为不下载,这里调用了刚刚引用的全局函数,.then得到的值是base64位的pdf文件
            .then((res) => {
              if (val) {
                console.log("准备上传");
                this.UploadPdf(res);
              } else {
                console.log("不上传");
              }
            });
        }, 1000);
      });
    },

下两个函数是上传文件的接口和base64转文件流的函数

由于是pdf的base64位至少需要1M,传给后端有些大,所以前端转成文件流formData形式传给后端

//上传pdf接口
    UploadPdf(res) {
     //res拿到base64的pdf
      let pdfBase64Str = res;
      let title ="上传给后端的个人报告"
      var myfile = this.dataURLtoFile(pdfBase64Str, title + ".pdf");//调用一下下面的转文件流函数
      var formdata = new FormData();
      formdata.append("file", myfile); // 文件对象
      //该uploadMy为接口,直接以formdata格式传给后台
      uploadMy(formdata)
        .then((res) => {
          console.log("上传pdf接口", res);
        })
        .catch((err) => {
          console.log("上传pdf接口", err);
        });
    },
    
/*
将base64转换为文件,接收2个参数,第一是base64,第二个是文件名字
最后返回文件对象
*/
   dataURLtoFile(dataurl, filename) {
      var arr = dataurl.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], filename, { type: mime });
    },

需要注意的是 上传文件的请求头为 ‘Content-Type’: ‘multipart/form-data’,post请求。

下面代码网络请求接口文件部分代码 为个人记录,不适用于所有人项目,且为部分代码。
通常做pdf下载有上面代码就足够了,下面可忽略

//api/index.js文件:

import $request from '@/utils/http'
let baseUrl = '/api/test'


if (process.env.NODE_ENV === 'development') {
  baseUrl = '/dev'
  bigDataUrl = '/service'
  collectUrl = '/collect'
}
// 上传文件-个人
export function uploadMy(data) {
  return $request.postUpload(baseUrl + '/common/upload', data)
}


//@/utils/http.js文件
import axios from './request'
/** post请求 lcl编写 请求头为上传的请求头*/
function postUpload(url, data,config) {
  return new Promise((resolve, reject) => {
    axios.post(url, data, config?config:{
     headers: {
        // 'Content-Type': 'application/x-www-form-urlencoded'
        'Content-Type': 'multipart/form-data'
      }
    }).then((res) => {
      // if (res.data.code === '801' || res.data.code === '802' || res.data.code === '804') {
      //   removeToken()
      //   router.push({ name: 'login' })
      // }
      resolve(res.data)
    }).catch((err) => {
      reject(err)
    })
  })
}



//@/utils/request.js文件

import axios from 'axios'
// import { Message, MessageBox } from 'element-ui'
import store from '@/store'
import { getToken, removeToken} from '@/utils/auth'

// create an axios instance

const service = axios.create({
  // baseURL: process.env.VUE_APP_APIURL, 
  // baseURL: "/api/test", 
  timeout: 20000 // request timeout  超过20s则失败
})

// request interceptor
service.interceptors.request.use(
  config => {
    // Do something before request is sent
    // 让每个请求携带token-- ['Token']为自定义key 请根据实际情况自行修改
    if(store.getters.token) {
      config.headers['Authorization'] = store.getters.token
    }
    return config
  },
  error => {
    Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  // response => response,
  /**
   * 下面的注释为通过在response里,自定义code来标示请求状态
   * 当code返回如下情况则说明权限有问题,登出并返回到登录页
   * 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
   * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
   */

  response => {
    const res = response.data
    if (res.code !== 200 && res.code !== 204) {
      // Message({
      //   message: res.msg,
      //   type: 'error',
      //   duration: 5 * 1000
      // })
      if (res.code === 401  || res.code === 501 || res.code === 804) {
        // 请自行在引入 MessageBox
        // import { Message, MessageBox } from 'element-ui'
        // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
        //   confirmButtonText: '重新登录',
        //   cancelButtonText: '取消',
        //   type: 'warning'
        // }).then(() => {
          removeToken();
          location.reload();
          store.dispatch('FedLogOut').then(() => {
            location.reload() // 为了重新实例化vue-router对象 避免bug
          });
          
        // })
      }
      return Promise.reject(res)
    } else {
      return response
    }
  },
  error => {
    // Message({
    //   message: error.msg,
    //   type: 'error',
    //   duration: 5 * 1000
    // })
    return Promise.reject(error)
  }
)

export default service

以上便是利用JsPDF和html2canvas先获取屏幕快照,生成pdf并以文件流形式上传到后端,默认生成的pdf为A4纸大小。  

标签:JsPDF,文件,vue,res,html2canvas,code,let,pdf,data
From: https://www.cnblogs.com/xuxuguaiguai/p/17134727.html

相关文章

  • vue项目打包
    登录dcloud开发者平台,注册一个账号链接https://dev.dcloud.net.cn/#/pages/common/login然后创建项目,写上项目名字,得到uniid回到项目点那个根目录的manifest.jso......
  • .NET6+WebApi+Vue 前后端分离后台管理系统(二)
    项目搭建: 这个项目使用的开发工具是:VSCode,工具的下载和安装这里就不赘述了,自行百度吧。使用的技术主要是:Vue3、ElementPlus等,Vue项目的搭建这里也不赘述,如果不熟悉可......
  • vue-element-admin改为从后台获取菜单
    一、修改文件\src\router\index.js文件的asyncRoutes清理为exportconstasyncRoutes=[{path:'*',redirect:'/404',hidden:true}]二、修改src\store\modul......
  • vue框架4
    购物车案例v-model进阶<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><scriptsrc="./js/vue.js"></script></h......
  • vue这些原理你都知道吗?(面试版)
    前言在之前面试的时候我自己也经常会遇到一些vue原理的问题,我也总结了下自己的经常的用到的,方便自己学习,今天也给大家分享出来,欢迎大家一起学习交流,有更好的方法......
  • 社招前端经典vue面试题(附答案)
    Vuex页面刷新数据丢失怎么解决体验可以从localStorage中获取作为状态初始值:conststore=createStore({state(){return{count:localStorage.getIt......
  • vueday5
    上节课回顾#0checkboxv-model只针对于input,做双向数据绑定 -单选:选中或不选中选中就是true,不选中就是false-多选:数组,选了多个,把选中的value值放到数......
  • 从零入门Vue.js!六步学习路线和知识体系盘点详解!
    Vue.js是一款流行的JavaScript前端框架,它允许开发者轻松地构建交互性强的用户界面。学习这个阶段的时候有一定门槛,并不是属于零基础就能入门学习的,在学习vue.js的时候可以......
  • uniapp nvue和vue 全局变量 国际化多语言开发 computed data globalData i18n undefin
    uni-app全局变量的几种实现方式1.公共模块定义一个专用的模块,用来组织和管理这些全局的变量,在需要的页面引入。注意:这种方式只支持多个vue页面或多个nvue页面之间公用,vue......
  • 购物车案例&v-model进阶&与后端交互&vue生命周期&vue组件
    目录购物车案例&v-model进阶&与后端交互&vue生命周期&vue组件今日内容概要今日内容详细1购物车案例1.1基本购物车1.2带全选全不选1.3带加减2v-model进阶3与后端交互3......