微信小程序实现一个文件管理器
虽然标题说是实现一个文件管理器,但我是用来管理预加载小程序可能用的网络资源的,尤其是要是用音效资源时。
小程序的包体即便是分包也是每个分包的资源大小是2m,像音频资源,使用场景往往是全局的,就是可能在每个页面都会用到,音频资源放在本地不是不可以,但是随着项目的拓展,包体渐渐变得不够用了,所以部分的资源需要放在云端来节省包体大小。
前期准备
基本是用到了微信小程序FileSystemManager的代码,对其进行了封装,文档地址:点这里。
实现流程:
输入一个数组,数组每一项都是一个对象,至少有一项是表示线上资源的地址,我这里直接命名为url。
对数组的每一项进行url置换处理:如果这个地址的文件已经下载过则替换成本地地址,如果没下载,就先下载再替换成本地地址。
返回一个完成替换的数组。
流程是比较简单的,但过程肯定也需要一容错的出来,下面就直接开始来实现吧~
实现
我们创建一个FileManager.js文件,并先创建一个FileManager类
const FileManager = (function(wx) {
// constructor
function FileManager() {
}
return FileManager
})(wx)
module.exports = new FileManager()
constructor先实例化一个FileSystemManager
function FileManager() {
if (typeof wx.getFileSystemManager === 'function') {
// 兼容
this.manager = wx.getFileSystemManager();
} else {
this.manager = null;
}
}
保存文件列表方法,我是期望在方法处理完将所有地址置换完后才返回,因为涉及到下载文件等一些异步的方法,我是返回了一个promise,并且做了一个兼容,如果客户端的版本不支持getFileSystemManager就原样返回
/**
* 线上文件换本地缓存文件
* @param filesList 文件列表
* @param dir 文件保存目录
*/
FileManager.prototype.saveFiles = function(filesList, dir) {
return new Promise((resolve, reject) => {
if (!this.manager) {
// 兼容
resolve(filesList)
return;
}
})
}
这个方法是有两个参数的,一个是文件列表,一个是希望保存的路径,为了防止在保存文件时,路径不存在报错的问题,我们要先做目录检查。FileSystemManager里有一个accessSync方法可以用于检查目录是否存在。
/**
* 检查目录是否存在
*/
FileManager.prototype.checkDir = function(dir) {
try {
this.manager.accessSync(dir);
return true
} catch (err) {
this.manager.mkdirSync(dir, true);
return false
}
};
解释一下,这里用try…catch是因为执行accessSync的时候,如果目录存在时是没有返回值的,但是如果目录不存在是直接报错,所以我们就可以根据是否有报错来判断目录是否存在。如果目录不存在,我们就调用mkdirSync方法来创建这个目录就可以了。
对于目录的处理,wx这个全局对象是有保存一个用户数据的保存路径的,在wx.env.USER_DATA_PATH,所以saveFiles方法中,dir参数只需要传具体的文件夹名,或者为空都可以,我们对它进行拼接就可以了
dir = `${wx.env.USER_DATA_PATH}/${dir}`
// 检查目录
this.checkDir(dir);
完成目录检查后,我们就可以开始做下载、保存文件的处理。
我们实现一个保存单个文件的方法saveFile
/**
* 保存单个文件
* @param url
* @param dir
* @param update
*/
FileManager.prototype.saveFile = function(url, dir, update = false) {
return new Promise((resolve, reject) => {
// 先检查文件是否存在
let name = getFileName(url);
let path = `${dir}/${name}`;
if (!update && this.checkFile(path)) {
// 如果需要update则直接去保存
// 文件存在,直接返回path
// console.log('文件已存在', path);
resolve(path);
} else {
// 下载文件并保存
downLoadFile(url).then(res => {
console.log('文件下载成功', res);
let tempFilePath = res.tempFilePath;
this.manager.saveFile({
tempFilePath,
filePath: path,
success: (res) => {
console.log('文件保存成功', res);
resolve(res.savedFilePath);
// saveFilePath
},
fail: (err) => {
// 失败的时候返回资源的地址
resolve(url);
console.log('文件保存失败', err);
}
})
}).catch(err => {
//
resolve(url);
console.log('文件下载失败', err)
})
}
});
};
这里主要做了三件事:
获取路径最后的文件名
检查文件是否存在
文件存在返回路径,不存在就先下载再返回路径,下载出错就返回原路径
getFileName就是一个正则返回url路径最后的文件名及后缀
/**
* 获取文件名称
* @param url
* @returns {string} --文件名
*/
function getFileName(url) {
let re = /\/([\w\.]+)$/;
let res = re.exec(url);
return res[1];
}
checkFile方法其实跟checkPath是一样的,只是文件不存在的时候不需要创建文件夹,这里就不解释了。
最后下载并保存那里,可以看到先用wx.downloadFile下载文件,然后将临时文件地址的文件保存到我们定义好的路径下,这里是用到了FileSystemManager.saveFile。保存这里简单来说,成功就返回本地地址,失败就返回原地址就好了。
我们回到saveFiles,执行实际的保存文件
/**
* 线上文件换本地缓存文件
* @param filesList 文件列表
* @param dir 文件保存目录
*/
FileManager.prototype.saveFiles = function(filesList, dir) {
return new Promise((resolve, reject) => {
// 兼容
if (!this.manager) {
// 不处理直接返回
resolve(filesList);
return
}
dir = `${wx.env.USER_DATA_PATH}/${dir}`;
// 检查目录
this.checkDir(dir);
// 开始保存文件
let finish_count = 0;
for (let i in filesList) {
// 文件地址
let url = filesList[i].url;
// 文件名
let name = filesList[i].name || getFileName(url);
// 保存文件,由于是异步的,所以要等文件列表都执行完才能resolve
this.saveFile(url, dir).then((path) => {
finish_count++;
filesList[i].url = path;
// 完成数等于文件列表长度
if (finish_count === filesList.length) {
// 返回更新的文件数组
resolve(filesList);
}
});
}
})
};
这里有个比较取巧的地方,就是saveFile的保存时机,我是用了计数的方法来实现的,毕竟你不知道这些文件哪个下载的比较快,所以只能等数量对了,就是finish_count === filesList.lenght,这时候来resolve就OK了
说一点拓展吧,既然我们单个文件的下载成功我们都能拿到,那我们就可以给FileManage注册一个onProcess时间,每成功一个文件,就回调一次,这样就可以监听到具体的下载情况了。
最后展示完整代码:
const FileManager = (function(wx) {
/**
*
* @constructor
*/
function FileManager() {
if (typeof wx.getFileSystemManager === 'function') {
this.manager = wx.getFileSystemManager();
} else {
this.manager = null;
}
}
/**
* 获取文件名称
* @param url
* @returns {string} --文件名
*/
function getFileName(url) {
let re = /\/([\w\.]+)$/;
let res = re.exec(url);
return res[1];
}
/**
* 下载文件,成功返回临时保存地址,失败返回原url
* @param url
* @returns {Promise}
*/
function downLoadFile(url) {
return new Promise((resolve, reject) => {
wx.downloadFile({
url,
success: (res) => {
resolve(res)
},
fail: (res) => {
reject(res)
}
})
})
}
/**
* 检查目录是否存在
*/
FileManager.prototype.checkDir = function(dir) {
try {
this.manager.accessSync(dir);
return true
} catch (err) {
this.manager.mkdirSync(dir, true);
return false
}
};
/**
* 检查文件是否存在
* @param path 文件目录
*/
FileManager.prototype.checkFile = function(path) {
// accessSync接口检查到目录不存在的时候会直接报错,这里用try catch来确定目录是否存在
try {
this.manager.accessSync(path);
return true
} catch (err) {
return false;
}
};
/**
* 保存单个文件
* @param url
* @param dir
* @param update
*/
FileManager.prototype.saveFile = function(url, dir, update = false) {
return new Promise((resolve, reject) => {
// 先检查文件是否存在
let name = getFileName(url);
let path = `${dir}/${name}`;
if (!update && this.checkFile(path)) {
// 如果需要update则直接去保存
// 文件存在,直接返回path
// console.log('文件已存在', path);
resolve(path);
} else {
// 下载文件并保存
downLoadFile(url).then(res => {
console.log('文件下载成功', res);
let tempFilePath = res.tempFilePath;
this.manager.saveFile({
tempFilePath,
filePath: path,
success: (res) => {
console.log('文件保存成功', res);
resolve(res.savedFilePath);
// saveFilePath
},
fail: (err) => {
// 失败的时候返回资源的地址
resolve(url);
console.log('文件保存失败', err);
}
})
}).catch(err => {
//
resolve(url);
console.log('文件下载失败', err)
})
}
});
};
/**
* 线上文件换本地缓存文件
* @param filesList 文件列表
* @param dir 文件保存目录
*/
FileManager.prototype.saveFiles = function(filesList, dir) {
return new Promise((resolve, reject) => {
// 兼容
if (!this.manager) {
// 不处理直接返回
resolve(filesList);
return
}
dir = `${wx.env.USER_DATA_PATH}/${dir}`;
// 检查目录
this.checkDir(dir);
// 开始保存文件
let finish_count = 0;
for (let i in filesList) {
// 文件地址
let url = filesList[i].url;
// 文件名
let name = filesList[i].name || getFileName(url);
// 保存文件,由于是异步的,所以要等文件列表都执行完才能resolve
this.saveFile(url, dir).then((path) => {
finish_count++;
filesList[i].url = path;
// 完成数等于文件列表长度
if (finish_count === filesList.length) {
// 返回更新的文件数组
resolve(filesList);
}
});
}
})
};
return FileManager
})(wx);
module.exports = new FileManager();
————————————————
版权声明:本文为CSDN博主「alanleung0421」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_33342614/article/details/100036917