前端生成word文件

2,242 阅读3分钟

需求介绍

在项目中,我们可以借助后端返回文件流实现文件下载。如果前端有数据,也可以借助前端框架进行下载。本文将介绍如何在前端纯js实现word文档生生成。

需求变化

根据产品需要,文件以word形式导出,并期望可以通过pdf形式实现预览。实现具体思路如下:

工具介绍

实现此功能需要使用到docxtemplaterjszip-utilsjszipFileSaver等js插件

  1. docxtemplater是一种邮件合并工具,它以编程方式使用,处理条件、循环,并且可以扩展为表格、HTML、图像等参考链接:docxtemplater.readthedocs.io/en/latest/i…
  2. jszip-utils是与jszip一起使用的跨浏览器的工具库。参考链接:stuk.github.io/jszip-utils…
  3. jszip是一个用于创建、读取和编辑.zip文件的JavaScript库,且API的使用也很简单。参考链接:stuk.github.io/jszip/
  4. FileSaver.js 是在客户端保存文件的解决方案,非常适合需要生成文件,或者保存不应该发送到外部服务器的敏感信息的应用。参考链接: www.cnblogs.com/yunser/p/76…

实现步骤

  1. 完成word模板 首先,根据需要导出的word文件的要求,先使用word制作出模板,数据使用{变量} 代替,注意:{}语法不能出现空格 image.png
  2. 安装依赖
  • cnpm install docxtemplater pizzip --save
  • cnpm install jszip-utils --save
  • cnpm install jszip --save
  • cnpm install file-saver --save
  1. 实现方式
  • 前端生成word文件--base64格式
  • 将base64转成文件传给后端,后端将文件解码后生成pdf文件
  • 前端调用查看文件接口获取base64文件流,通过iframe实现文件预览

引入js文件

import PizZip from "pizzip"
import JSZipUtils from "jszip-utils"
import { sendReport } from '@/api/grouping'
import { Message } from 'element-ui'

/**
 导出docx
 1. @param { String } tempDocxPath 模板文件路径
 2. @param { Object } data 文件中传入的数据
 3. @param { String } fileName 导出文件名称
 4. @param { group_id } group_id 导出文件id--可选参数
 5. @param { group_id } callback 导出文件后的回调函数--可选参数

*/
export const exportDocx = (tempDocxPath, data, fileName, group_id, type, callback) => {
  // 读取并获得模板文件的二进制内容exportDocx
  JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
    if (error) {
      throw error
    }
    const zip = new PizZip(content)
    const doc = new Docxtemplater().loadZip(zip)
    doc.setData(data)
    try {
      doc.render()
    } catch (error) {
      const e = {
        message: error.message,
        name: error.name,
        stack: error.stack,
        properties: error.properties,
      }
      console.log({
        error: e,
      })
      throw error
    }
    const out = doc.getZip().generate({
      type: "blob",
      mimeType:
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    })

    function getBase64(out) {
      let reader = new FileReader()
      return new Promise((resolve) => {
        reader.readAsDataURL(out)
        return reader.onload = function (e) {
          resolve(base64toFile(e.target.result))
        }
      })
    }

    getBase64(out).then((res) => {
      let params = {
        simulationID: group_id,
        report_type: 'score',
        report_data: res,
        type: 'sc'
      }
      sendReport(params).then((res) => {
        callback()
        Message.success('生成成功')
      })
    })

    // 第一个参数dataUrl是一个base64的字符串。第二个参数是文件名可以随意命名
    function base64toFile(dataurl, filename = 'file') {
      let arr = dataurl.split(',');
      let mime = arr[0].match(/:(.*?);/)[1];
      // suffix是该文件的后缀
      let suffix = mime.split('/')[1];
      // atob 对经过 base-64 编码的字符串进行解码
      let bstr = atob(arr[1]);
      // n 是解码后的长度
      let n = bstr.length;
      // Uint8Array 数组类型表示一个 8 位无符号整型数组 初始值都是 数子0
      let u8arr = new Uint8Array(n);
      // charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      // new File返回File对象 第一个参数是 ArraryBuffer 或 Bolb 或Arrary 第二个参数是文件名
      // 第三个参数是 要放到文件中的内容的 MIME 类型
      return new File([u8arr], `${filename}.${suffix}`, {
        type: mime,
      });
    }

    // saveAs(out, fileName)
    // window.URL.createObjectURL(out)
  })
}

pdf文件预览iframe式

<iframe width="100%" height="100%" :src="pdfUrl" name="成绩评定报告" frameborder="0"></iframe>

      let params = {
        type: "ck",
        simulationID: row.group_id,
        report_type: "score",
      };
      sendReport(params).then((res) => {
        this.reportTitle = row.group_id + "成绩评定报告";
        this.openViewPdf = true;
        let url = "data:application/pdf;base64," + res.pdf;
        this.pdfUrl = url;
        // window.open();
        // document.open();
        // document.write(iframe);
        // document.close();
      });
    },