为什么有这种需求
有时候我们需要在网页进行 office 文本编辑,但是 office 文件的编辑选项过多且不开源,一定需要集成其它在线编辑方案才可以,比如金山文档的在线协作方案
但是有时候我们并不需要如此健壮的成熟方案,很多时候我们对一个现在 office 模版只需要修改其中几项的文字就可以了。这种情况下,我们只需要写一个现成的表单,像问卷一样填写相关内容,把填写的内容再转入到 office 文件中就可以了。
如上,现在我希望用户在我的网页上只填写 name 与 id
之后网页会将 xlsx 文件中对应占位符替换成用户填写的对应内容,这个需求可以通过 xlsx-template 来实现
项目版本
测试项目为Vue项目,Vue版本为3,webpack版本为5(重点,xlsx-template库与这个版本有冲突,后面讲解决方案),包管理器为 yarn(npm也可以),测试文件类型为 typeScript(使用javaScript也可以,稍作修改即可)
代码
安装 xlsx-template:
npm i xlsx-template 或 yarn add xlsx-template
安装 jszip-utils:
npm i jszip-utils 或 yarn add jszip-utils
安装 file-saver:
npm i file-saver 或 yarn add file-saver
// @ts-ignore
import JSZipUtils from 'jszip-utils'
import {saveAs} from 'file-saver'
执行代码:
const XlsxTemplate = require('xlsx-template');
JSZipUtils.getBinaryContent('/xlsx/template1.xlsx',(err: any, data: Buffer) => {
const template = new XlsxTemplate(data);
// Replacements take place on first sheet
const sheetNumber = 1;
// Set up some placeholder values matching the placeholders in the template
const values = {
name: '人人人',
id: 888888
};
// Perform substitution
template.substitute(sheetNumber, values);
// Get binary data
let templateData = template.generate({type: 'blob'});
saveAs(templateData, 'new.xlsx')
})
vue.config.js 设置
const {defineConfig} = require('@vue/cli-service')
const webpack = require('webpack')
module.exports = defineConfig({
lintOnSave: false,
transpileDependencies: true,
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
})
],
resolve: {
fallback: {
"path": require.resolve('path-browserify'),
"util": require.resolve("util/"),
"stream": require.resolve("stream-browserify"),
"constants": require.resolve("constants-browserify"),
"assert": require.resolve("assert/"),
"fs": false,
}
}
}
})
注意:上文中类似库名的地方,除了fs和buffer外都需要使用 npm 或 yarn 安装对应的库
比如:npm i util 与 yarn add assert
需要加载的库名如下:
webpack process path-browserify util stream-browserify constants-browserify assert
vue.config.js 讲解
因为 xlsx-template 库是在 webpack4 的基础上打包的,在 webpack5 的项目上出现在非常多的错误,故需要如上 vue.config.js 的设置。
上面的设置虽少,但是解决了至少4个类型的错误,共数十个具体错误
- Can't resolve 'fs' 问题
这个问题其实是因为 fs 库已经无法直接调用,而在 xlsx-template 代码却有调用
使用 resolve.fallback: { fs: false } 可以跳过编译检测
解决自:https://github.com/webpack-contrib/css-loader/issues/447#issuecomment-761853289
- process is not defined 问题
这个问题可能是因为在 webpack5 中 process 已经无法在项目获取,需要使用外部引用并转换成同名引用
使用 new webpack.ProvidePlugin({ process: 'process/browser' }) 可以解决
- 各种 webpack < 5 开头的问题
这种问题在控制台已经说明了解决方法,只是可能很多人不知道具体应该把解决代码写在哪里,上图中的 resolve.fallback: { ... } 中除了 fs:false 其它都是会出现问题的部分的解决方法
这个问题仍然是 webpack 版本造成的,但是不能像 fs 一样忽略,必须注入
解决自:https://stackoverflow.com/questions/64557638/how-to-polyfill-node-core-modules-in-webpack-5
- Cannot read properties of undefined (reading 'filename') 或 buffer 问题
在vue3项目上会报 “Cannot read properties of undefined”,但是在我的另一个vue2项目中会直接报找不到 buffer 函数问题,所以这是个无法调用到 buffer api 的问题
解决自:https://stackoverflow.com/questions/66156756/how-to-polylfill-buffer-for-jsonwebtoken-in-wepack-5
成果
写在后面
上文中的测试代码只有加载本地 xlsx 文件,如果需要从服务器下载再替换,无论您使用什么网络加载框架都需要注意:
xlsx-template 框架需要传入 buffer 类型数据,所以请做转换,或直接找以 buffer 类型下载的网络框架
xlsx-template 的 generate 函数注意传入 { type: 'blob' } 这样直接可以保存成 blob 类型数据,方便后面向服务器进行网络上传
为了避免后续文中出现的库有更新,使本文内容出现偏差,提供本测试项目的 package.json 中各个库的版本号:
"assert": "^2.0.0",
"constants-browserify": "^1.0.0",
"core-js": "^3.8.3",
"docxtemplater": "^3.37.11",
"file-saver": "^2.0.5",
"jszip-utils": "^0.1.0",
"path": "^0.12.7",
"pizzip": "^3.1.4",
"stream-browserify": "^3.0.0",
"util": "^0.12.5",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"webpack": "^5.88.1",
"xlsx-template": "^1.4.3"