最近一直在查找在微信小程序中生成Excel的办法。需求就是根据一个json数据或者对象数组,生成一个Excel文件,或者打开Excel文件。网上找了很久,没找到很有效的解决办法。最后自己动手做了一个,不过生成效率还比较低。
我比较赞同的方法有两种,第一种是使用api来实现。如果你有个服务器,那么一切就好办了。只需要把数据传给服务器,然后服务器生成Excel文件,并返回文件的url。之后通过wx.downloadFile下载文件,配合wx.openDocument打开文件即可。至于如何编写一个返回Excel文件url的api,方法就太多了。
但是不是所有人都有服务器,所以还得相关离线的办法生成Excel文件。我的思路是,Excel文件本身是一个压缩文件,我们只需要安装Excel的目录结构,逐个文件和文件夹创建,如何将文件压缩为xx.xlsx文件即可。
结果查找,腾讯狗没有提供压缩相关的api。不过js倒是有现成jszip库可以使用,有了jszip,我们就可以开始实现了。
先下载jszip库,然后将dist中的js文件放到小程序下的utils目录下。
在pages/index/index.wxml中写个按钮:
<view class="container">
<view>
<button bindtap="createXLSX_zip">生成压缩包button>
view>
view>
然后在pages/index/index.js中最上面导入JSZip:
const JSZip = require("../../utils/jszip");
并编写响应函数:
createXLSX_zip() {
const fs = wx.getFileSystemManager();
let zip = new JSZip();
const content = [
{name: 'zack', age: 21, gender: 'male'},
{name: 'alice', age: 20, gender: 'female'},
{name: 'atom', age: 23, gender: 'male'},
{name: 'rudy', age: 22, gender: 'male'},
]
let contentXML = "";
let idx = 0
//按照Excel的规则,生成xml字符串
let sheetData = `
`
let keys = Object.keys(content[0])
for (let i = 0; i < keys.length; i++) {
contentXML += ` ${keys[i]}`
sheetData += `${String.fromCharCode(i + 'A'.charCodeAt())} 1" t="s"> ${idx}`
idx += 1
}
sheetData += ''
for (let i = 0; i < content.length; i++) {
sheetData += `${i + 2}
" span="1:3">`
for (let j = 0; j < keys.length; j++) {
contentXML += ` ${content[i][keys[j]]}`
sheetData += `${String.fromCharCode(j + 'A'.charCodeAt())} ${j + 2}" t="s"> ${idx}`
idx += 1
}
sheetData += ""
}
wx.showLoading({
title: '正在生成',
})
zip.folder("docProps")
zip.folder("xl")
zip.folder("_rels")
zip.file("[Content_Types].xml", '\n ')
zip.file(`docProps/app.xml`, '\nSheetJS 工作表 1 Sheet1 ')
zip.file(`docProps/core.xml`, '\nivx 2022-07-11T07:23:00Z 2022-07-11T07:25:43Z ')
zip.file(`docProps/custom.xml`, '\n784038AB04864B7497D2D5CF431A9FF0 2052-11.1.0.11830 ')
zip.folder(`xl/theme`)
zip.folder(`xl/worksheets`)
zip.folder(`xl/_rels`)
zip.file(`xl/sharedStrings.xml`, `\n${keys.length * (content.length + 1)} " uniqueCount="${keys.length * (content.length + 1)}">${contentXML}`)
zip.file(`xl/styles.xml`, '\n ')
zip.file(`xl/workbook.xml`, '\n ')
zip.file(`xl/theme/theme1.xml`, '\n ')
zip.file(`xl/worksheets/sheet1.xml`, `\n ${sheetData} `)
zip.file(`xl/_rels/workbook.xml.rels`, '\n ')
zip.file(`_rels/.rels`, '\n ')
zip.generateAsync({type: "arraybuffer"})
.then(function (content) {
fs.writeFileSync(`${wx.env.USER_DATA_PATH}/result.xlsx`, content, 'utf-8');
console.log(typeof (content));
console.log(content);
wx.hideLoading({
success: (res) => {
wx.openDocument({
filePath: `${wx.env.USER_DATA_PATH}/result.xlsx`,
showMenu: true,
fileType: 'xlsx',
success: function (res) {
console.log('打开文档成功', res)
}
})
},
})
})
}
Excel的具体结构可以把后缀改成zip,然后解压查看。上面大多数文件都使用了默认的内容,只有xl/worksheets/sheet1.xml和xl/sharedStrings.xml两个文件进行了修改。其中sheet1.xml是表的具体数据,而sharedStrings.xml类似于一个词典,为了节约内存,单元格重复内容不会存储两次。不过这里我没有去考虑单元格重复的问题。
使用上面的方法在Android和iOS都成功跑通了,不过速度不是很快。
另外,这里提供一个替代方案。就是不生成Excel,转而生成csv文件。虽然不能完全替代Excel,但是也能有表格的效果。代码如下:
const fs = wx.getFileSystemManager();
wx.showLoading({
title: '正在生成',
})
let dataStr = ""
let keys = Object.keys(tableData[0])
//标题
for (let i = 0; i < keys.length; i++) {
if (i != keys.length - 1) {
dataStr += (keys[i] + ",")
} else {
dataStr += (keys[i] + "\n")
}
}
//数据
for (let i = 0; i < tableData.length; i++) {
for (let j = 0; j < keys.length; j++) {
if (j != keys.length - 1) {
dataStr += (tableData[i][keys[j]] + ",")
} else {
dataStr += (tableData[i][keys[j]] + "\n")
}
}
}
wx.showLoading({
title: '正在写入',
})
fs.writeFileSync(`${wx.env.USER_DATA_PATH}/data.csv`, dataStr, "utf-8")
wx.hideLoading({
success: (res) => {
wx.showModal({
title: '成功',
content: '生成完成,是否分享',
success(res) {
if (res.confirm) {
wx.shareFileMessage({
filePath: `${wx.env.USER_DATA_PATH}/data.csv`,
success() {
},
fail: console.error,
})
} else if (res.cancel) {
}
}
})
},
})
不过wx.openDocument不支持csv文件,所以想到用其它应用打开。但是腾讯狗也没有提供其它程序打开的api,所以这里生成后分享文件。腾讯真狗!