Hbuilder打包H5开发的App读取写入手机本地文件
在平时开发的html页面中,我们写的Js是没有读取用户电脑本地文件的权限的,这是出于浏览器运行时的安全考虑的,但在我们在使用h5打包app时,如果再像浏览器上让用户下载,上传文件,就会使用户的使用体验远不如电脑上操作,而且经常会有一些想要缓存本地的数据,cookies太小,满足不了需求,所以就想到能不能有内容时,我能直接操作手机的存取,我使用的是hbuilder打包app的,所以到官网上找找,就找到了有一节专门关于io的介绍,研究了一下使用方法,所以特此记录一下
本文所参考官方文档 https://www.html5plus.org/doc/zh_cn/io.html
先明确几个主要对象及方法,这里与java里的对照来看,会java的朋友可能会更容易理解
requestFileSystem 请求本地文件系统对象的方法,获取指定的文件系统,可通过type指定获取文件系统的类型。 获取指定的文件系统对象成功通过succesCB回调返回,失败则通过errorCB返回。
参数:
-
- type: ( Number ) 必选 本地文件系统常量
- 可取plus.io下的常量,如plus.io.PRIVATE_DOC、plus.io.PUBLIC_DOCUMENTS等。
- succesCB: ( FileSystemSuccessCallback ) 必选 请求文件系统成功的回调
- errorCB: ( FileErrorCallback ) 可选 请求文件系统失败的回调
注意,这里的type取值共4个,对应于不同的空间,也有不同的访问权限限制,一定要注意区分,如果读取或写入文件时报错时,记得检查这个地方, 以下为原文
为了安全管理应用的资源目录,规范对文件系统的操作,5+ API在系统应用目录的基础设计了应用沙盒目录, 分为私有目录和公共目录两种类型,私有目录仅应用自身可以访问,公共目录在多应用环境时(如小程序SDK)所有应用都可访问。
-
- 应用私有资源目录,对应常量plus.io.PRIVATE_WWW,仅应用自身可读
- 应用私有文档目录,对应常量plus.io.PRIVATE_DOC,仅应用自身可读写
- 应用公共文档目录,对应常量plus.io.PUBLIC_DOCUMENTS,多应用时都可读写,常用于保存应用间共享文件
- 应用公共下载目录,对应常量plus.io.PUBLIC_DOWNLOADS,多应用时都可读写,常用于保存下载文件
DirectoryEntry 文件系统中的目录对象,用于管理特定的本地目录或者文件,对应于java中可以理解为File对象,这是操作文件最基础的对象,一个directoryEntry对象就对应于一个文件或文件夹
属性:
-
- isFile: 操作对象的是否为文件,DirectoryEntry对象固定其值为false
- isDirectory: 操作对象是否为目录,DirectoryEntry对象固定其值为true
- name: 目录操作对象的名称,不包括路径
- fullPath: 目录操作对象的完整路径,文件系统的绝对路径
- fileSystem: 文件操作对象所属的文件系统对象,参考FileSystem
方法:
-
- getMetadata: 获取目录的属性
- moveTo: 移动目录
- copyTo: 拷贝目录
- toURL: 获取目录路径转换为URL地址
- toLocalURL: 获取目录路径转换为本地路径URL地址
- toRemoteURL: 获取目录路径转换为网络路径URL地址
- remove: 删除目录
- getParent: 获取目录所属的父目录
- createReader: 创建目录读取对象
- getDirectory: 创建或打开子目录
- getFile: 创建或打开文件
- removeRecursively: 递归删除目录
FileEntry 文件系统中的文件对象,用于管理特定的本地文件,对应于java中可以理解为File对象
属性:
-
- isFile: 文件操作对象的是否为文件,FileEntry对象固定其值为true
- isDirectory: 文件操作对象是否为目录,FileEntry对象固定其值为false
- name: 文件操作对象的名称,不包括路径
- fullPath: 文件操作对象的完整路径,文件系统的绝对路径
- fileSystem: 文件操作对象所属的文件系统对象,参考FileSystem
方法:
-
- getMetadata: 获取文件的属性信息
- moveTo: 移动文件
- copyTo: 拷贝文件
- toURL: 获取文件路径转换为URL地址
- toLocalURL: 获取文件路径转换为本地路径URL地址
- toRemoteURL: 获取文件路径转换为网络路径URL地址
- remove: 删除文件
- getParent: 获取文件所属的父目录
- createWriter: 获取文件关联的写文件操作对象FileWriter
- file: 获取文件数据对象
FileReader 创建读取文件对象,主要是文件读取相关的操作,文件以文本或者Base64编码的字符串形式读出来,对应于java中可理解为InputStream
属性:
-
- readyState: 当前读取文件所处的状态
- result: 已读取文件的内容
- error: 文件操作错误代码
方法:
-
- abort: 终止文件读取操作
- readAsDataURL: 以URL编码格式读取文件数据内容
- readAsText: 以文本格式读取文件数据内容
事件:
-
- onloadstart: 读取文件开始时的回调函数
- onload: 读取文件成功完成的回调函数
- onabort: 取消读取文件时的回调函数
- onerror: 文件读取操作失败时调用的回调函数
- onloadend: 文件读取操作完成时的回调函数
FileWriter 文件系统中的写文件对象,用于写入文件内容,用户注册自己的事件监听器来接收writestart、progress、write、writeend、error和abort事件,一个FileWriter对象是为单个文件的操作而创建,可以使用该对象多次对相应文件进行写入操作。 FileWriter维护该文件的指针位置及长度属性,这样就可以寻找和写入文件的任何地方。 默认情况下,FileWriter从文件的开头开始写入(将覆盖现有数据),seek方法可设置文件操作指定位置,如fw.seek(fw.length-1)写入操作就会从文件的末尾开始 ,对应于java中理解为OutputStream
属性:
-
- readyState: 当前写入文件所处的状态
- length: 文件当前的长度,单位为字节
- position: 文件当前操作的指针位置
- error: 文件写入操作错误代码
方法:
事件:
-
- onwritestart: 写入文件开始时的回调函数
- onwrite: 写入文件成功完成的回调函数
- onabort: 取消写入文件时的回调函数
- onerror: 文件写入操作失败时调用的回调函数
- onwriteend: 文件写入操作完成时的回调函数
下面是读写文件的主要代码及说明
1、获取文件读写的基础,FileEntry对象
1 //读取应用公共文档目录 2 plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) { 3 // 通过fs.root获取DirectoryEntry对象进行操作,获取文件操作的根目录 4 //这里的filePath即你要读取的文件所在的相对路径,可随意定义,但不得是 _www开头,因为_www开头是应用私有资源目录的专用,只有读权限,不能写入,写入时会报错 5 var filePath = 'abc/haha/test.txt'; 6 var rootDirectoryEntry = fs.root; 7 rootDirectoryEntry.getFile(filePath, { 8 //这个参数的作用是 指示如果文件或目录不存在时是否进行创建,默认值为false,设为true表示如果这个filePath下的test.txt文件不存在就创建,当然,如果存在就直接返回,不会创建 9 create: true 10 }, function(fileEntry) { 11 //FileEntry对象获取成功,对应就是test.txt文件了,可以接着进行相应的读写操作了 12 13 }, function(e) { 14 console.log(e.message); 15 }); 16 }, function(e) { 17 console.log(e.message); 18 });
2、在上一步获取到文件操作对象FileEntry的基础上,开始读操作
1 //获取文件 2 fileEntry.file(function (file) { 3 //console.log(file.size + ' <--> ' + file.name); 4 //创建一个文件读取工具,在java中理解就是InputStream输入流 5 var fileReader = new plus.io.FileReader(); 6 //成功读取到文件内容时的回调,其中evt.target.result就是文件中的文本内容 7 fileReader.onloadend = function (evt) { 8 console.log(evt.target.result); 9 } 10 //文件读取操作失败时调用的回调函数 11 fileReader.onerror = function (e) { 12 console.log(e.message); 13 } 14 //将刚才请求到的文件以utf-8编码,文本的形式读出 15 fileReader.readAsText(file, 'utf-8'); 16 });
3、还是以文件操作对象FileEntry为基础,进行写入文件的操作
1 //通过fileEntry的createWriter创建输出流,向文件写入内容,对应java中的OutputStream 2 fileEntry.createWriter(writer => { 3 //文件写入成功后的回调 4 writer.onwrite = function(event) { 5 //写入成功 6 console.log('写入成功'); 7 } 8 //文件写入操作失败时调用的回调函数 9 writer.onerror = function(e) { 10 console.log(e.message); 11 } 12 //设置文件写入的起点,writer.length就是上次文件里面内容的最后位置,这样即将新的内容追加到文本最末尾,如果想覆盖原先的内容,直接设置为0即可 13 var cursor = writer.length; 14 writer.seek(cursor); 15 //将要写入的文本dataStr写入到文件中去 16 writer.write(dataStr); 17 }, function(e) { 18 console.log(e.message); 19 });
以上就是在Hbuilder中打包H5的App读写文件的主要代码了及功能了
但是我们发现这样写会有无数的回调函数,不仅不好看,别人调用时也不好传参,所以我们可以采用ES6的写法加上Primse来封装改造一下,让这些方法看起来更加优雅一点
1 getFileEntry(filePath) { 2 return new Promise((resolve, reject) => { 3 plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, fs => { 4 fs.root.getFile(filePath, {create: true}, fileEntry => { 5 resolve(fileEntry); 6 }, e => { 7 reject(e) 8 }); 9 }, e => { 10 reject(e) 11 }); 12 }) 13 }, 14 readData(fileEntry) { 15 return new Promise((resolve, reject) => { 16 fileEntry.file(function (file) { 17 let fileReader = new plus.io.FileReader(); 18 fileReader.onloadend = function (evt) { 19 resolve(evt.target.result); 20 } 21 fileReader.onerror = function (e) { 22 reject(e) 23 } 24 fileReader.readAsText(file, 'utf-8'); 25 }); 26 }) 27 }, 28 writeData(fileEntry, dataStr, cursorStart) { 29 return new Promise((resolve, reject) => { 30 fileEntry.createWriter(writer => { 31 writer.onwrite = e => { 32 resolve() 33 } 34 writer.onerror = e => { 35 reject(e) 36 } 37 //设置文件写入的起点 38 let length = cursorStart != undefined ? cursorStart : writer.length; 39 writer.seek(length); 40 writer.write(dataStr); 41 }, e => { 42 reject(e) 43 }); 44 }) 45 }
这样在调用时,代码就会非常简洁了
读取文件内容
1 let filePath = '/abc/haha/test.json'; 2 this.getFileEntry(filePath) 3 .then(fileEntry => { 4 // fileEntry.remove(); //删除文件 5 return this.readData(fileEntry); 6 }).then(fileText => { 7 alert("文件内容:>>> " + fileText) 8 }).catch(e => { 9 alert('文件读取失败!') 10 })
写入文件内容
1 let writeDataStr = 'Hello World!!!' 2 this.getFileEntry() 3 .then(fileEntry => { 4 return this.writeData(fileEntry, writeDataStr); 5 }).then(writer => { 6 alert('保存成功'); 7 }).catch(e => { 8 alert('保存失败!') 9 })
OK ! 打完收工