微信小程序使用自定义目录(文件路径)进行下载/保存 案例(fail permission denied 解决方案)
场景描述
最近项目中有一个需要把网络文件下载下来保存到本地,然后对下载的文件进行读取,待文件不再使用后把文件进行删除的需求。当然也类似的需求还有很多,比如把小程序中的临时图片/文件永久保存下来等等,都是对文件操作的典型场景。
常见问题
在以上场景的实现过程中可能会遇到各式各样的问题,以下是比较常见的几个:
不清楚文件应该保存到哪个目录下。
fail permission denied 文件权限问题。
使用同步函数不清楚怎么获取执行结果。
API提炼
提到文件操作我们会自然而然地想到了API中的FileSystemManager相关的API,我这里用到的函数有以下几个:
下载函数
wx.downloadFile(Object object)
异步函数:
FileSystemManager.access(Object object)
FileSystemManager.mkdir(Object object)
同步函数:
FileSystemManager.accessSync(string path)
FileSystemManager.mkdirSync(string dirPath, boolean recursive)
我对同样的业务逻辑分别分别尝试了异步和同步的两种不同的方案,下面我们用一个最简单的下载保存到本地的案例来切入正题。
案例实践
一:获取正确的文件目录路径
当然在保存文件之前我们先要解决一个小问题,那就是我们要保存到哪里?也就是我们自定义的目录。这里我们简单命名其为
[代码]//自定义缓存文件根路径
var rootPath = "......";
[代码]
(当然你也可以命名成其他名字)
变量名字可以随便写,不过自定义路径可不能随便写,也不可以在下载的时候直接给一个path路径,否者就会抛出没有权限或找不到文件的异常。所以开发者并不是可以随意的决定自定义文件的路径,这里就不卖关子了,小程序API中有一个很容易被忽略的API,
wx.env.USER_DATA_PATH是专门获取文件系统中的用户目录路径的常量值。
这就是我们在小程序中合法的可操作文件的根目录路径:
[代码]rootPath = wx.env.USER_DATA_PATH;
[代码]
好了到目前为止我们已经知道了我们该往里存储文件了。
定义一下我们下载文件的缓存目录
[代码]var cachePath = rootPath+"/cache";
[代码]
也就是说之后我们下载的文件都会保存到 /cache 目录下,我们在之后的代码中用到的目录路径均为此路径。
二:异步函数实现方案
我们先来用异步函数来实现一下下载保存的流程。
1 下载文件之前我们首先要判断当前目录是否存在,如果目录不存在我们就直接下载文件到该目录下就会抛出 fail permission denied
[图片]
这是判断目录存在的代码
[代码] access() {
return new Promise(function(resolve, reject) {
let fm = wx.getFileSystemManager();
fm.access({
path: cachePath,
success: function(res) {
resolve();
},
fail: function(err) {
resolve(err);
}
});
});
},
[代码]
2 如果目录真实存在那我们当然可以直接使用,如果目录不存在则需要开发者自己创建目录。
[代码] mkdir(){
return new Promise(function(resolve, reject) {
let fm = wx.getFileSystemManager();
fm.mkdir({
dirPath: cachePath,
recursive: true,
success: function(res) {
resolve();
},
fail: function(err) {
resolve(err);
}
});
});
},
[代码]
代码执行完之后我可以验证一下目录是否如我们所愿被创建出来。
开发工具右上角的详情–>基本信息–>文件系统–>当前小程序文件系统根目录
[图片]
点击usr文件夹进入根目录
[图片]
执行完上面代码之后我们可以看一下 cache 文件夹确实已经存在了
[图片]
3 目录也存在了,万事具备只欠下载了
[代码] downloadFile() {
let fileUrl = 'https://res.wx.qq.com/wxdoc/dist/assets/img/0.4cb08bb4.jpg';
wx.downloadFile({
url: fileUrl,
filePath: cachePath + '/temp.png',
success: function(res) {
console.log('downloadFile success', res);
},
fail: function(err) {
console.log('downloadFile fail', err);
}
});
},
[代码]
那执行完下载的代码之后我们再来看看cache目录
[图片]
这就是我们刚刚下载的图片。
ok,异步的整个下载和文件创建流程就走完了。接下来我们来瞅瞅同步流程中有哪些需要我们注意的。
三: 同步方案
同步方案和异步方案的流程大体一致,都是先判断文件目录是否存在,若不存在则创建目录,存在则执行下载逻辑。
使用同步函数需要特别注意的是怎么去判断函数的执行结果,由于
FileSystemManager.accessSync(string path)
FileSystemManager.mkdirSync(string dirPath, boolean recursive)
这两个同步函数没有直接给出任何的返回结果。那我们怎么知道目录是否存在、目录是否被创建成功了呢?
这里我们需要剑走偏锋一下,即利用同步函数抛出的异常来判断结果。
我们直接来看代码
[代码] accessSync() {
return new Promise(function(resolve, reject) {
let fm = wx.getFileSystemManager();
try {
fm.accessSync(cachePath);
resolve();
} catch (err) {
resolve(err);
}
});
},
mkdirSync() {
return new Promise(function(resolve, reject) {
let fm = wx.getFileSystemManager();
try {
fm.mkdirSync(cachePath, true);
resolve();
} catch (err) {
resolve(err);
}
});
},
[代码]
可以看到我们的代码中多了 try catch 的代码结构,因为同步方法中没有直接返回给我们可用的信息,那我们可以认为同步函数正常执行完的结果为true或success,而进入 catch 后则结果为false或fail亦或根据具体异常具体处理。我们利用同步函数来走一遍下载保存的流程。
[代码] // 同步函数流程
this.accessSync().then(function (err) {
if (err) {
return that.mkdirSync();
}
}).then(function (err) {
if (!err) {
that.downloadFile()
}
});
[代码]
可以看到我们利用同步函数下载的图片。
[图片]
总结时刻
我们分别使用异步和同步函数完成了目录的创建和文件的下载等流程。在这个过程中我们特别需要注意几点:
操作文件的根目录是以 wx.env.USER_DATA_PATH 开头的。
使用自定义目录时一定主要不可直接使用,需要增加 判断目录存在、创建目录 两个步骤。
使用同步函数时的执行结果是通过抓取同步函数抛出的异常来进行判断的。在没有给出直接结果的时候要学会利用异常信息来达到目的。
在创建目录时如果该目录存在同样会抛出异常(fail file already exists),这时按照success逻辑继续往下执行即可。
异步方案中介绍了目录查看的步骤和方法,可自行验证。其中usr目录是开发者自定义目录根节点,tmp目录是小程序默认的缓存根节点。
结尾
叙述若有不对或不严谨之处还请不吝指正。