html2pdf无法导出图片解决方案(2020版)

研究背景:最近做的项目中有功能项是导出PDF报告,由于页面内容包含多种统计图,后台人员不好实现,那就前端来呗~ 由于是内网项目,图片都读取服务器,导致下载pdf中图片出不来,我太南了。

---------------------------------------------------------------------------分割线------------------------------------------------------------------------------

导Pdf方法大家应该都知道了,先把html转成canvas,再转成pdf。

由于项目用的vue框架+typescript语言开发,提前在这里声明一下,方便各位朋友先做语法了解。

用到了第三方库 html2canvas和jspdf,网上都有这里就不贴地址了(或者没事的时候再贴)。

先说一下发现问题的思路吧:

1.一开始啥都不提示,我以为被盖住了,但是把pdf背景色去掉,还是没有,说明图片是真没导出来。

2.用本地项目图片试试,成功了(这很重要,下面会讲);用网络图片试试,也导出来了,那会不会是跨域的问题?

3.找网上资料,有说修改 useCORS 改为true允许跨域,allowTaint改为true的,现在把图片的路径固定写死一张服务器图片,还真就提示了

破案了破案了,嫌犯终于承认了。哎~别慌,怎么回事呢,去看第二条

html2canvas没拿到图片的资源。

由此,大体知道了症结:

普通的img标签src属性链接服务器图片,是不会引起跨域问题的,这也是一开始没想到的原因,那咋会报跨域?稍微看看(万一没看懂说错了呢)就能知道,html转canvas时对html里的图片、echartscanvas又单独做了一次处理,他用js方式引的图片,才造成了跨域问题。

但是咋搞呢,推荐一款神奇的网站,名叫【百度一下,你就知道】(咳咳,彦宏老哥,记得打钱)

在网上搜了如下内容

HTML转为PDF,图片导出失败的终极解决方案

作者用下载到本地再引用的方法,在此向作者致敬,把跨域请求改成本地文件,这不就解决了?但我不想搞这么麻烦,有没有更好的办法?

用过vue开发的小伙伴都知道,vue引用本地图片的方式是require('项目相对路径'),到了页面其实就是转成了base64码

我能不能直接把base64码放在图片的src属性上,这样相当于动态存放到我本地,就不会出现跨域了?导出看看,结果是成功了,那么我只需要在每次导出时把动态图片地址转成base64码就可以了,do it。

参考 vue将图片网络地址转base64格式

上代码

data(){
    imgSrc: null
}
mounted(){
    //...执行你的查询方法获取图片网络路径,仅为了演示所以写成回调方式
    this.search().then(()=>{
        this.toBase64('图片全路径')
    })
}

methods(){
    toBase64(imgUrl: any) {
        try {
          let image = new Image()
          // 解决跨域问题
          image.setAttribute('crossOrigin', 'anonymous')
          let imageUrl = imgUrl
          image.src = imageUrl
          // image.onload为异步加载
          image.onload = () => {
            let canvas = document.createElement('canvas')
            canvas.width = image.width
            canvas.height = image.height
            let context: any = canvas.getContext('2d')
            context.drawImage(image, 0, 0, image.width, image.height)
            let quality = 0.8
            // 这里的dataurl就是base64类型
            // 使用toDataUrl将图片转换成jpeg的格式,不要把图片压缩成png,因为压缩成png后base64的字符串可能比不转换前的长!
            let dataurl = canvas.toDataURL('image/jpeg', quality)
            this.imgSrc = dataurl
          }
        } catch (e) {
          console.log(e)
        }
      }
}

页面上直接赋值即可

<img :src="imgSrc">

完全不会影响到html2pdf代码,这样导出的内容就能看到图片了。

到这里,我们站在巨人们的肩膀上,搞定这个问题啦,和巨人们的思路都是化跨域为本地,不过vue框架的好处就体现出来了,哈哈哈。

下面上vue+TS的关键代码

import html2Canvas from 'html2canvas'
export default class SingleHealthAnalysisModal extends Vue{
    imgSrc: any = null

    created() {
        this.initBasicInfo(this.equipId)
    }
    
    render(){
        <div>
            <img
               style={
   
   { width: '100%', height: '100%' }}
               src={this.imgSrc ? this.imgSrc : require('@/assets/images/default_pic.jpg')}>
            </img>
            <a-button
                type="primary"
                on-click={() => {
                    this.$AntMessage.loading({ duration: 0, content: '文件导出中...' })
                    this.printOut('装备健康情况分析')
                }}
              >
            导出PDF
            </a-button>
        </div>
    }
    
    initBasicInfo(equipId:any){
        // 查询详情请求
        .then((res:any)=>{
            if (res.code === window.CROSS_CODE) {
                this.imgSrc = null // 重置
                this.equipBasicInfo = res.map
                if (this.equipBasicInfo.equipmentImage && this.equipBasicInfo.equipmentImage !== 'null') {
                    this.toBase64(window.config.imgShowUrl + this.equipBasicInfo.equipmentImage.split(',')[0])
                }
            }
        })
    }

  printOut(name: any) {
    try {
      let shareContent: any = document.querySelector('.allLifeDetail') //需要截图的包裹的(原生的)DOM 对象
      let width = shareContent.clientWidth //获取dom 宽度
      let height = shareContent.clientHeight //获取dom 高度
      let canvas: any = document.createElement('canvas') //创建一个canvas节点
      let scale = 1 //定义任意放大倍数 支持小数
      canvas.width = width * scale //定义canvas 宽度 * 缩放
      canvas.height = height * scale //定义canvas高度 *缩放
      canvas.style.width = shareContent.clientWidth * scale + 'px'
      canvas.style.height = shareContent.clientHeight * scale + 'px'
      canvas.getContext('2d').scale(scale, scale) //获取context,设置scale
      let opts = {
        scale: scale, // 添加的scale 参数
        canvas: canvas, //自定义 canvas
        logging: false, //日志开关,便于查看html2canvas的内部执行流程
        width: width, //dom 原始宽度
        height: height,
        useCORS: true, // 【重要】开启跨域配置
        // allowTaint: true,
        // taintTest: true,
        // logging: true,
      }

      html2Canvas(shareContent, opts).then(() => {
        var contentWidth = canvas.width
        var contentHeight = canvas.height
        //一页pdf显示html页面生成的canvas高度;
        var pageHeight = (contentWidth / 592.28) * 841.89
        //未生成pdf的html页面高度
        var leftHeight = contentHeight
        //页面偏移
        var position = 0
        //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
        var imgWidth = 595.28
        var imgHeight = (592.28 / contentWidth) * contentHeight
        var pageData = canvas.toDataURL('image/jpeg', 1.0)
        var PDF = new jsPDF('', 'pt', 'a4')
        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()
            }
          }
        }
        this.$AntMessage.destroy()
        PDF.save(name + '.pdf') // 这里是导出的文件名
      })
    } catch (error) {
      this.$AntMessage.destroy()
      this.$AntMessage.error('导出失败')
    }
  }

  toBase64(imgUrl: any) {
    try {
      imgUrl = imgUrl.replace('\\', '/')
      // 一定要设置为let,不然图片不显示
      let image = new Image()
      // 解决跨域问题
      image.setAttribute('crossOrigin', 'anonymous')
      let imageUrl = imgUrl
      image.src = imageUrl
      // image.onload为异步加载
      image.onload = () => {
        var canvas = document.createElement('canvas')
        canvas.width = image.width
        canvas.height = image.height
        var context: any = canvas.getContext('2d')
        context.drawImage(image, 0, 0, image.width, image.height)
        var quality = 0.8
        // 这里的dataurl就是base64类型
        // 使用toDataUrl将图片转换成jpeg的格式,不要把图片压缩成png,因为压缩成png后base64的字符串可能比不转换前的长!
        let dataurl = canvas.toDataURL('image/jpeg', quality)
        this.imgSrc = dataurl
      }
    } catch (e) {
      console.log(e)
    }
  }
}

参考文章:

HTML转为PDF,图片导出失败的终极解决方案

vue将图片网络地址转base64格式

Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may n

猜你喜欢

转载自blog.csdn.net/rrrrroy_Ha/article/details/109990259