• 前端实现生成图片并批量下载,下载成果物是zip包


    简介

    项目上有个需求,需要根据表单填写一些信息,来生成定制的二维码图片,并且支持批量下载二维码图片。
    之前的实现方式是直接后端生成二维码图片,点击下载时后端直接返回一个zip包即可。但是项目经理说后端实现方式每次改个东西都要改大半天,所以让前端来实现。

    方案

    1.后端返回二维码的base64url数据流,就是下图红框中的二维码图片。

    在这里插入图片描述
    2.前端负责展示成交互上的二维码图片样式,如下图。

    在这里插入图片描述
    3.点击批量下载时,用户自己选择下载数量,然后后端返回二维码base64url的数组,前端自己实现下载,且是以zip的形式下载。下载的每张图片都是前端页面上所展示的样子。

    思路

    比如批量下载50个,首先10个一组处理,每个二维码编号生成一个blob流,塞入生成的zip中,50个二维码编号全部处理完成后,开始下载zip,将zip转为blob流,触发下载等待下载完成。

    耗时较久的是每个二维码编号生成一个blob流,需要前端拿到后端返回的二维码base64 url, 通过js代码组装成最终的图片样式的DOM,然后需要塞到页面中,再使用dom-to-image 转成图片形式的blob流。

    实现

    1.比如选择下载数量是50张,点击下载,触发handleDownload 函数
    2.使用jszip 生成一个zip
    3.print_set_root 是该页面组件中最外层的div元素
    4.分10个一组进行处理

    完整代码如下:

    import JSZip from 'jszip'
    import { chunk } from 'lodash'
    import domtoimage from 'dom-to-image'
    
    async handleDownload (val) {
      this.downBtnLoading = true
      try {
        const { data } = await downQuas({ count: Number(val), randomNum: 6, start: this.ruleForm.code })
        this.zip = new JSZip()
        const rod = document.getElementById('print_set_root')
        const arr = chunk(data, 10)
        for (let i = 0; i < arr.length; i++) {
          await this.usePromiseArr(arr[i], rod)
        }
        this.downBtnLoading = false
        const that = this
        this.zip.generateAsync({ type: 'blob' }).then(function (base64) {
          const url = URL.createObjectURL(base64)
          const link = document.createElement('a')
          link.download = `${that.regionName}.zip`
          link.href = url
          link.click()
          setTimeout(() => { window.URL.revokeObjectURL(url) })
        })
      } catch (e) {
        this.downBtnLoading = false
      }
    },
    usePromiseArr (data, rod) {
      const allPromise = []
      data.forEach(v => {
        allPromise.push(this.renderImg(v, rod))
      })
      return Promise.all(allPromise)
    },
    renderImg (data, rod) {
      return new Promise((resolve, reject) => {
        let num = 0
        const useSrc = `data:image/png;base64,${data.value}`
        const template2 = `
        
    报修电话
    ${this.ruleForm.phoneNumber || 'xxxxxxxx'}
    `
    const leftDiv = document.createElement('div') leftDiv.setAttribute('class', 'left downLeft') leftDiv.setAttribute('id', 'erweima-common') const header = document.createElement('div') header.setAttribute('class', 'left-header') const large1 = document.createElement('div') large1.setAttribute('class', 'font-large') large1.textContent = 'xxxx' const large2 = document.createElement('div') large2.setAttribute('class', 'font-large') large2.textContent = 'xxxx' const topImage = document.createElement('div') topImage.setAttribute('class', 'top-img') const img1 = document.createElement('img') img1.src = '/xxxxxx.png' img1.onload = () => { topImage.appendChild(img1) num++ this.downloadImg(num, leftDiv, rod, data.key, resolve) } header.appendChild(large1) header.appendChild(topImage) header.appendChild(large2) leftDiv.appendChild(header) const safe = document.createElement('div') safe.setAttribute('class', 'safe') safe.textContent = 'xxxxxxxxxx' const borderDiv = document.createElement('div') borderDiv.setAttribute('class', 'left-border') const dashedDiv = document.createElement('div') dashedDiv.setAttribute('class', 'dashed-border') const Img2 = document.createElement('img') Img2.src = '/xxxxxxxx.png' Img2.onload = () => { dashedDiv.appendChild(Img2) num++ this.downloadImg(num, leftDiv, rod, data.key, resolve) } const title1 = document.createElement('div') title1.setAttribute('class', 'title') title1.textContent = 'xxxx' const title2 = document.createElement('div') title2.setAttribute('class', 'title') title2.textContent = 'xxxxxxxxxx' const title3 = document.createElement('div') title3.setAttribute('class', 'title-min') title3.textContent = 'Area Under 24-hour Monitoring' borderDiv.appendChild(dashedDiv) borderDiv.appendChild(title1) borderDiv.appendChild(title2) borderDiv.appendChild(title3) const border2 = document.createElement('div') border2.setAttribute('class', 'left-border') border2.innerHTML = template2 const small = document.createElement('div') small.setAttribute('class', 'title-small') small.textContent = `${this.ruleForm.producer || 'xxxxxxxxx'}` const leftcontent = document.createElement('div') leftcontent.setAttribute('class', 'left-content') const useImg = document.createElement('img') useImg.setAttribute('class', 'erwei') useImg.src = useSrc useImg.onload = () => { leftcontent.appendChild(useImg) leftcontent.appendChild(safe) leftcontent.appendChild(borderDiv) leftcontent.appendChild(border2) leftcontent.appendChild(small) leftDiv.appendChild(leftcontent) rod.appendChild(leftDiv) num++ this.downloadImg(num, leftDiv, rod, data.key, resolve) } }) }, downloadImg (num, leftDiv, rod, name, resolve) { if (num !== 3) return const that = this domtoimage.toBlob(leftDiv).then(function (dataUrl) { rod.removeChild(leftDiv) that.zip.file(`${name}.jpeg`, dataUrl) resolve() }) }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    使用技术:dom-to-image JSZip

    注意点:

    1. 元素在appendChild图片时,一定要等到图片onload后再执行appendChild操作。
    2. 元素最终塞到页面上渲染时,注意下别让用户看到,可以 absolute + z-index 修改显示层级。
  • 相关阅读:
    贪心算法 Greedy Algroithm
    arduino(esp8266)驱动74hc595进行流水灯异常一例
    用 PHP 构建安全的 Web 应用程序
    推荐系统笔记(十七):对超图、超图卷积、超图注意力的初步理解和应用(HyperGCN)
    【java】idea可以连接但看不到database相关的files
    电厂数据可视化三维大屏展示平台加强企业安全防范
    基于AAEncode编码的解密经历
    ROS入门21讲笔记
    如何建设一个安全运营中心(SOC)?
    [WPF]用HtmlTextBlock实现消息对话框的内容高亮和跳转
  • 原文地址:https://blog.csdn.net/weixin_47359038/article/details/136561277