场景
微信小程序只提供了解压ZIP的API,并没有提供创建ZIP的方法。
当我们想把自己处理好的保存,打包ZIP保存下来时就需要自己实现了。
分享代码片段
不想听废话的,直接看代码
https://developers.weixin.qq.com/s/ChblKjmo7ZNd
导入 JSZip
首先需要 jszip
const JSZip = require('../lib/jszip.min');
const fs = wx.getFileSystemManager();
jszip
在微信小程序无法直接跑。要处理一下。把 setImmediate
全部替换为 setTimeout
创建ZIP文件
/**
* 文件打包为 zip
* @param {*} fileList 文件列表 [{ name: '文件名', path: '文件路径'}]
* @param {*} zipPath 保存压缩包的路径
* @param {*} progress 处理进度更新时:回调
*/
async function zip(fileList, zipPath, progress=res=>console.log){
try {
// 实例化 jszip
var jszip = new JSZip();
// 遍历文件列表,添加到 zip 文件列表中
let total = fileList.length;
fileList.forEach((file, index) => {
jszip.file(file.name, new Uint8Array(fs.readFileSync(file.path)));
progress({ percent: Math.round((index+1)/total) * 100, msg: `读取资源 ${index+1}/${total}` });
});
// 生成压缩包对象(uint8array)
let content = await jszip.generateAsync(
{ type : JSZip.support.uint8array ? "uint8array" : "string" },
meta => progress({ percent: Math.floor(meta.percent), msg: `创建 ZIP...` }) // { currentFile: '', percent: 100 }
);
// 将 arrayBuffer 形式压的缩包数据写入二进制文件,生成 zip
progress({ percent: 0, msg: `保存 ZIP...` }); // 开始
// 分块写入
await appendFile({
filePath: zipPath,
data: content.buffer,
encoding: 'binary',
progress: percent => progress({ percent: Math.floor(percent), msg: `保存 ZIP...` })
}).then(() => {
progress({ percent: 100, msg: `ZIP 保存完成` });
}).catch(err => {
console.error('写入文件失败:', err);
});
} catch (err) {
console.log(err);
}
}
追加写入文件
/**
* 追加写入文件
* @param {string} filePath 文件路径
* @param {string} data 写入数据
* @param {string} encoding 编码类型:默认 utf8
* @param {number} chunkSize 写入块大小:默认 1048576 字节
* @param {function} progress 更新进度回调
*/
function appendFile(options) {
let { filePath, data, encoding, chunkSize, progress } = Object.assign({
encoding: 'utf8', // 编码类型,默认 utf8。想写二进制用 'binary'
chunkSize: 1048576, // 每块大小默认 1M
progress: console.log // 更新进度
}, options);
// 文件总长度
const fileLength = data instanceof ArrayBuffer ? data.byteLength : data.length;
// 文件小于 chunkSize 直接写
if(fileLength <= chunkSize){
return new Promise((resolve, reject) => {
try {
resolve(fs.writeFileSync( filePath, data, encoding ));
} catch (error) {
reject(error);
}
});
}else{
// 否则分块写入,并调用进度更新 callback
return new Promise((resolve, reject) => {
// 先写入一个空文件。(作用:有则清空,无则创建)
fs.writeFileSync(filePath, new ArrayBuffer(0), encoding);
// 已写入长度
let writtenLength = 0;
// 写入数据块
const writeChunk = () => {
const chunkData = data.slice(writtenLength, writtenLength + chunkSize); // 切段
fs.appendFile({
filePath, // 文件路径
data: chunkData, // 数据块
encoding, // 编码类型
success: () => {
writtenLength += chunkSize; // 更新已写入长度
progress( Math.floor((writtenLength / fileLength) * 100)); // call回调函数更新进度
if (writtenLength < fileLength) {
writeChunk(); // 继续写入下一块数据
} else {
resolve(writtenLength); // 文件写入完成:返回写入长度
}
},
fail: err => reject(err)
});
};
// 继续调用写入数据块
writeChunk();
});
}
}
测试方法
test(){
const zipFolder = `${wx.env.USER_DATA_PATH}/test`;
const zipPath = `${zipFolder}/hello.zip`;
let fileList = [];
try {
// 先创建对应目录
fileUtil.mkdir(zipFolder);
// 生成测试文件
for (let index = 0; index < 10; index++){
let filePath = `${wx.env.USER_DATA_PATH}/test/hello${index}.txt`;
fileList.push({ name: `hello${index}.txt`, path: filePath});
const res = fs.writeFileSync( filePath, `测试数据${index+1}`, 'utf8' );
console.log(res);
}
// 打包 zip
fileUtil.zip(fileList, zipPath, console.log);
// 保存 zip
wx.saveFileToDisk({ filePath: zipPath, success: console.log, fail: console.error });
} catch(e) {
console.error(e)
}
}
测试效果
保存 ZIP 懒得开真机调试录动图了。功能是OK的。
开发环境查看生成的 zip
- 先打开开发环境的文件系统所指向的目录
- 找到
${wx.env.USER_DATA_PATH}
对应的目录
- 根据我们自定的路径找到生成的测试文件