用到的生成库
officegen github.com/Ziv-Barber/…
当然也有下载了更高,版本迭代更多的,文档全面的docx 。对比文档来看officegen的api更简单方便。
原理
当把.docx 文件更改后缀名为.zip 。解压之后可以看到这样的文件结构
其中 document.xml 就是内容区域,styles.xml存储了一些样式
这个库就是主要把内容生成 document.xml的标签,然后通过jszip打包。最后将生成好的文档呈现出来
除了docx,ppt,xls等文档也是可以通过这个库生成。
服务端
生成Docx部分,使用了 createByJson这个API ,这个API可以使用一个JSON描述的文档结构直接生成成为Docx文档,所以在浏览器端只需要关注怎么样生成这个结构即可。
const generateDocx = async (
HTTPStream,
data,
options = {
orientation: "portrait" || "landscape",
author: "",
creator: "",
description: "",
keywords: "",
pageMargins: { top: 1800, right: 1440, bottom: 1800, left: 1440 },
pageSize: "A4",
}
) => {
return new Promise((resolve, reject) => {
let docx = officegen({ type: "docx", ...options });
docx.createByJson(data);
docx.on("finalize", function (written) {
console.log(
"Finish to create a Word file.\nTotal bytes created: " + written + "\n"
);
resolve();
});
docx.on("error", function (err) {
reject();
console.log("// Error handing...");
});
docx.generate(HTTPStream);
});
};
使用 koa搭建HTTP服务
const Koa = require("koa");
const router = require("koa-router")();
const static_ = require("koa-static");
const bodyparser = require("koa-bodyparser");
const { PassThrough } = require("stream");
var app = new Koa();
var port = 3001;
app.use(static_(path.join(__dirname, "./public/")));
/**
* 使用koa-router 来定义一个post请求
* 接收 nodedata 和 options 两个参数。
* https://github.com/Ziv-Barber/officegen/blob/master/manual/docx/README.md#the-document-objects-settings
*/
router.post("/api/docx.gen", async (ctx, next) => {
const { nodedata = [], options = {} } = ctx.request.body;
ctx.set(
"Content-Type",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
ctx.set("Content-disposition", "attachment; filename=demo.docx");
const stream = new PassThrough();
ctx.body = stream;
ctx.status = 200;
generateDocx(stream, nodedata, options);
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(port);
http服务就初步搭建成功,可以跑起来之后访问 localhost:3001/api/docx.gen 这个接口了
客户端
1,获取到要生成的html节点
2,将节点转换成 createByJson这个API需要的格式
// 将HTML字符串转成DOM对象
const officeGen = (html) => {
const Parser = new DOMParser();
const doc = Parser.parseFromString(html, "text/html");
const nodes = doc.body.childNodes;
return nodes2array(nodes);
};
// 将所有的dom节点转成需要的格式
function nodes2array(nodes) {
var Root = [];
for (var i = 0; i < nodes.length; i++) {
possiblyAddNodeToResult(Root, nodes[i], {});
}
return Root;
}
// 递归处理节点 生成数组
function possiblyAddNodeToResult(result = [], node, attr = {}) {
//...
}
//... 篇幅太长了可以下载demo查看
3,调用接口 获取文件流
const getDocx = () => {
const html = document.querySelector("#report").innerHTML;
const nodedata = officeGen(html);
fetch("/api/docx.gen", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
nodedata,
options: {
orientation: "landscape",
},
}),
})
.then((res) => res.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
window.open(url);
});
};
4,保存文件到本地
在第三步是直接用的URL.createObjectURL 生成url再新窗口打开,但是这样生成的文档名称是随机的。所以完成请求之后可以使用saveAs这些第三方库保存。
不是很完美的生成方案
像基本的字体样式 表格都能够支持了。图片canvas,需要单独处理,转成图片上传到服务端或者用base64方式,但是都会增加服务器压力。后面就尝试一下另外个库 docx的生成效果。