在Vue中下载包含图片、图表的docx文档

848 阅读2分钟

插件使用

  • npm i @types/pizzip
  • npm i docxtemplater
  • npm i jszip-utils
  • npm i file-saver
  • npm i docxtemplater-image-module-free
  • npm i echarts

最近的公司的一个需求,需要下载带有图片的文档,找了半天,看了文档只能下载普通的,下载图片啥的需要收费,一年几百欧,然后没办法,先把图片这个模块暂时搁置了,然后在隔了半个月后,看到了一个demo,抱着试一试的想法,运行起了代码,然后就解决了这个每年需要多花费几百欧的需求。

/* html部分 */
<template>
  <div id="docxExport">
    <div @click="exportWord">
      点击下载文档
    </div>
  </div>
</template>

html测试用的非常简单,不要在意

/* js */
import PizZip from 'pizzip'
import Docxtemplater from 'docxtemplater'
import JszipUtils from 'jszip-utils'
import FileSave from 'file-saver'
import ImageModule from 'docxtemplater-image-module-free'
import * as echarts from 'echarts'

插件的引入,如果只需要下载普通的文档,只需要引入前面4个插件就行 E1@{M0AXC6X4KQEWIZMYS.jpg

第五个插件是用来下载图片、图表用的;第六个就是专门用来渲染图表用的

  data() {
    return {
      img: 'https://img-cloud.youjiaoyun.net/mp/0a802a40-4a4b-4121-aa88-1fc6367a7410.jpg',
      img2: 'http://bhyf-file.oss-cn-hangzhou.aliyuncs.com/4578/1636526930976_4fb0c795.jpeg',
      childs: [
        {
          name: '张三',
          age: '18'
        },
        {
          name: '李四',
          age: '19'
        }
      ],
      imglist: [
        'http://bhyf-file.oss-cn-hangzhou.aliyuncs.com/4578/1636526930976_4fb0c795.jpeg',
        'https://img-cloud.youjiaoyun.net/mp/0a802a40-4a4b-4121-aa88-1fc6367a7410.jpg'
      ],
      pnglist: []
    }
  },
/**
 * 下载图片什么需要将格式转化为base64
 * 图片转base64方法
 */
urlToBase64(url) {
  return new Promise((resolve) => {
    const image = new Image()
    image.setAttribute('crossOrigin', 'Anonymous')
    image.onload = function() {
      const canvas = document.createElement('canvas')
      canvas.width = image.width
      canvas.height = image.height
      canvas.getContext('2d').drawImage(image, 0, 0)
      const result = canvas.toDataURL('image/png')
      resolve(result)
    }
    image.src = url
  })
},

/**
 * base64转为ArrayBuffer
 * 这个我也不知道为什么,我看最后下载打印的时候连接是base64格式的
 */
base64DataURLToArrayBuffer(dataURL) {
  const base64Regex = /^data:image\/(png|jpg|svg|svg\+xml);base64,/
  if (!base64Regex.test(dataURL)) {
    return false
  }
  const stringBase64 = dataURL.replace(base64Regex, '')
  let binaryString
  if (typeof window !== 'undefined') {
    binaryString = window.atob(stringBase64)
  } else {
    binaryString = new Buffer(stringBase64, 'base64').toString('binary')
  }
  const len = binaryString.length
  const bytes = new Uint8Array(len)
  for (let i = 0; i < len; i++) {
    const ascii = binaryString.charCodeAt(i)
    bytes[i] = ascii
  }
  return bytes.buffer
},
/**
 * 导出word
 */
async exportWord() {
  const _this = this
  const pngs = []
  this.imglist.forEach((v) => {
    pngs.push(this.urlToBase64(v))
  })
  try {
    const pnglist = await Promise.all(pngs)
    this.pnglist = pnglist.map((v) => {
      return {
        url: v
      }
    })
  } catch (error) {
    console.log(error)
  }
  //  echarts图表
  var div = document.createElement('div')
  div.setAttribute('style', 'width: 400px;height:200px;')
  let option = {
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: [
      {
        data: [150, 230, 224, 218, 135, 147, 260],
        type: 'line'
      }
    ]
  }
  let myChart = echarts.init(div)
  myChart.setOption(option)

  // 将文件转为二进制
  JszipUtils.getBinaryContent('news.docx', function(err, res) {
    console.log(res)
    if (err) {
      throw err
    }
    let opts = {
      centered: false,
      fileType: 'docx'
    }
    opts.getImage = function(tagValue) {
      if (tagValue.size && tagValue.data) {
        return _this.base64DataURLToArrayBuffer(tagValue.data)
      }
      return _this.base64DataURLToArrayBuffer(tagValue)
    }
    opts.getSize = function(_, tagValue) {
      if (tagValue.size && tagValue.data) {
        return tagValue.size
      }
      return [200, 200] // word里面图片的尺寸
    }
    // 将文件转为zip文件
    let pizZip = new PizZip(res)
    let doc = new Docxtemplater()
    doc.loadZip(pizZip)
    doc.setOptions({
      nullGetter: function() {
        return ''
      }
    })
    console.log(opts)
    doc.attachModule(new ImageModule(opts))
    // 设置填充内容
    let a = {
      image: myChart.getDataURL({
        pixelRatio: 5, // 导出的图片分辨率比率,默认是1
        backgroundColor: '#afdfe4', // 图表背景色
        excludeComponents: ['toolbox'], // 忽略组件的列表
        type: 'png' // 图片类型支持png和jpeg
      }),
      imgs: _this.pnglist
    }
    doc.setData(a)
    // 进行内容填充
    try {
      doc.render()
    } catch (error) {
      // 抛出异常
      let e = {
        message: error.message,
        name: error.name,
        stack: error.stack,
        properties: error.properties
      }
      console.log(e)
      throw error
    }
    // 获取要下载的文件
    let out = doc.getZip().generate({
      type: 'blob',
      mimeType:
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    })
    // 进行下载
    FileSave(out, '带有图片的docx下载')
  })
}

word格式

image.png

{%image} 就是正常的出现 {%%image} 居中出现 {#imgs}{url}{/imgs} 循环渲染

大致就是这样,里面还有些地方不是很明白,不过代码和人有一个能跑不就行了HYU7L1G21BEJH7UMI~0(N9.jpg