微信小程序实现一个文件管理器

微信小程序实现一个文件管理器

虽然标题说是实现一个文件管理器,但我是用来管理预加载小程序可能用的网络资源的,尤其是要是用音效资源时。

小程序的包体即便是分包也是每个分包的资源大小是2m,像音频资源,使用场景往往是全局的,就是可能在每个页面都会用到,音频资源放在本地不是不可以,但是随着项目的拓展,包体渐渐变得不够用了,所以部分的资源需要放在云端来节省包体大小。

前期准备
基本是用到了微信小程序FileSystemManager的代码,对其进行了封装,文档地址:点这里

实现流程:

  1. 输入一个数组,数组每一项都是一个对象,至少有一项是表示线上资源的地址,我这里直接命名为url。
  2. 对数组的每一项进行url置换处理:如果这个地址的文件已经下载过则替换成本地地址,如果没下载,就先下载再替换成本地地址。
  3. 返回一个完成替换的数组。

流程是比较简单的,但过程肯定也需要一容错的出来,下面就直接开始来实现吧~

实现
我们创建一个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();
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值