纯前端生成doc文件,支持zip批量下载

540 阅读1分钟

前言需求

  1. 根据表格字段生成对应的doc模板文件【如合同、录取通知书等】
  2. 支持批量下载doc,生成zip压缩文件

为什么要前端生成文件?

一般情况下,生成文件是由后端生成二进制文件,前端获取二进制文件进行浏览器下载。
但是,假如有多个用户,同时请求服务器进行资源的处理和下载,服务器承受的压力会相应变大。
所以,把压力分配到各个用户的浏览器上,服务器只返回数据给前端处理。

  • 一般情况
graph LR
后端处理Data --> 后端根据文件类型生成对应的二进制 --> 前端通过api获取二进制 --> 实现浏览器下载
  • 纯前端情况
graph LR
后端处理Data --> 前端通过api获取Data --> 根据需求把Data转换成对应的文件 --> 实现浏览器下载

npm包

开发

  1. 准备一个模板文件template.docx。 文档内容以{}括起来的为我们的变量名,如{name}

image.png

  1. 安装必要包。这里我使用pnpm
pnpm i docxtemplater pizzip file-saver
  1. 封装单个文件、zip文件的下载主要代码
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils/index.js";
import { saveAs } from "file-saver";

...
interface User {
  name: string
  grade: string
  grade_code: string
  departments: string
  year: string
  month: string
}

// vite获取本机文件
function getAssetsImages(name: string) {
  return new URL(`/src/assets/${name}`, import.meta.url).href;
}

// 通过PizZip获取文件二进制内容
function loadFile(url: string, callback: Function) {
  PizZipUtils.getBinaryContent(url, callback);
}

// 批量生成
async function bathCreate() {
  if (multipleSelection.value.length === 0) {
    return ElMessage('Please choose!')
  }
  // 输出的zip文件
  const bathZip  = new PizZip()
  for (let index = 0; index < multipleSelection.value.length; index++) {
    const item = multipleSelection.value[index];
    // 把选中的进行文档创建,并添加到bathZip压缩文件里
    await createDoc(bathZip, item)
  }
  // 转换成blob文件
  const content = bathZip.generate({ type: "blob" });
  // 浏览器下载
  saveAs(content, "example.zip");
}

/**
 * @name 封装单个文件下载
 * @param bathZip 批量输出的zip文件
 * @param item 用户信息
 */
function createDoc(bathZip: any, item: User) {
  return new Promise((resolve) => {
    loadFile(
      getAssetsImages("template.docx"),
      (error: Error, content: ArrayBuffer) => {
        if (error) {
          throw error;
        }
        // 获取模板内容,通过pizzip进行转换
        const zip = new PizZip(content);
        
        const doc = new Docxtemplater(zip, {
          paragraphLoop: true, // 一般开启,建议打开该选项,因为它使渲染更容易推理。
          linebreaks: true, // 启用换行符
        });
        // 设置模板数据
        doc.setData(item);
        try {
          // 进行模板替换渲染
          doc.render();
        } catch (error) {
          throw error;
        }
        // 以base64输出文件
        const out = doc.getZip().generate({
          type: "base64",
          mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
        });
        // 以name进行文件命名,并把文件添加到zip文件里
        bathZip.file(`${item.name}.docx`, out, { base64: true });
        resolve(true)
      }
    );
  })
}
  1. 实现效果

9a59286ba6bbbdc73fdb980977e2cfa.png

image.png