实现一个web端的word模板导入及数据渲染导出
背景
在工作中遇到一个需求,需要用户自己创建一个word模板,然后上传到网页端再把我们的数据渲染word对应的地方并导出。
翻了半天github决定用以下技术实现:
- 用PizZip来实现数据的上传和打包 github.com/open-xml-te…
- 用Docxtemplater来实现对文档中的变量进行赋值和渲染 github.com/open-xml-te…
- 用FileSaver来实现文档数据的本地保存 github.com/eligrey/Fil…
一个最简单的实现
Docxtemplater可以获取到导入的word数据中用特殊符号(例如‘{}’)包裹的变量,并给其赋值,从而实现对模板的赋值。
以下是用angular的一个简单实现:
import { Component } from '@angular/core';
import * as docxtemplater from 'docxtemplater';
import * as inspect from 'docxtemplater/js/inspect-module';
import * as PizZip from 'pizzip';
import * as pizzipUtils from 'pizzip/utils';
import * as FileSaver from 'file-saver';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
// 加载文件
loadFile(url, callback) {
pizzipUtils.getBinaryContent(url, callback); // 将文件加载成二进制文件
}
generate() {
this.loadFile('../assets/test.docx', function(error, content) {
if (error) { throw error; }
const zip = PizZip(content); // 将内容转化PizZip对象
const doc = new docxtemplater().loadZip(zip); // 获得templater对象
// 对word中的变量进行赋值
doc.setData({
title: '模板测试',
anthor: 'elc',
time: '2019-10-2',
content: `docxtemplater is a mail merging tool that is used programmatically and handles conditions,
loops, and can be extended to insert anything (tables, html, images).`
});
try {
doc.render(); // 将赋值渲染到word数据中
} catch (error) {
const e = {
message: error.message,
name: error.name,
stack: error.stack,
properties: error.properties,
};
console.log(JSON.stringify({error: e}));
throw error;
}
// 生成输出数据,数据类型为FileSaver插件可以识别的blob类型,还可以生成string, base64, uint8array, arraybuffer类型
console.log(doc.getZip().generate)
const out = doc.getZip().generate({
type: 'blob'
});
FileSaver.saveAs(out, 'output.docx'); // 保存,并输出
});
}
}
这是传入的文件模板:

这是导出文件的结果:

导入循环数据
导入循环数据的模板用‘{#}’ 和‘{/}’包裹。 例如:

doc.setData({
heros: [
{
heroName: 'Lina',
heroCamp: 'radiant',
heroEquipment: 'drogon'
},
{
heroName: 'Earth Shocker',
heroCamp: 'radiant',
heroEquipment: 'blink dragger'
},
{
heroName: 'Lion',
heroCamp: 'Dire',
heroEquipment: 'drogon'
}
]
});

这样的循环可以用在表格中

doc.setData({
heroTable: [
{
hero_name: 'Lina',
hero_camp: 'radiant',
hero_equipment: 'drogon'
},
{
hero_name: 'Earth Shocker',
hero_camp: 'radiant',
hero_equipment: 'blink dragger'
},
{
hero_name: 'Lion',
hero_camp: 'Dire',
hero_equipment: 'drogon'
},
]
});

按条件导入数据
#开头的变量可以添加条件来控制导入, {/}可以表示或,来进行多种条件的控制。不过这种方法需要导入angular parse插件并设置模板才能使用。 如下例:

doc.setData({
users: [
{
name: 'John'
},
{
name: 'Mary'
},
{
name: 'Jane'
},
{
name: 'Sean'
}
]
});

小结
只要使用基本用法和循环数据,起始就可以完成几乎所有的模板制作。官方另外提供了一些table,html,grid等更加便捷的插件,不过要收费。。。
一个容易踩的坑,在word的模板中不允许任何空格{title}和{ title }是两个变量。
要动态获取模板变量的话,需要导入inspect module来解析数据:
import * as inspect from 'docxtemplater/js/inspect-module';
getTages(doc: docxtemplater) {
const iModule = inspect();
doc.attachModule(iModule);
doc.render();
const tags = iModule.getAllTags();
console.log(tags);
return tags;
}

其他更详细的东西请参阅文档 docxtemplater.readthedocs.io/en/latest/i…
转载自:https://juejin.cn/post/6844903940933287943