Hbuilder打包H5开发的App读取写入手机本地文件

时间:2024-02-22 19:58:32

在平时开发的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)所有应用都可访问。

 

 

DirectoryEntry 文件系统中的目录对象,用于管理特定的本地目录或者文件,对应于java中可以理解为File对象,这是操作文件最基础的对象,一个directoryEntry对象就对应于一个文件或文件夹

属性:

    • isFile: 操作对象的是否为文件,DirectoryEntry对象固定其值为false
    • isDirectory: 操作对象是否为目录,DirectoryEntry对象固定其值为true
    • name: 目录操作对象的名称,不包括路径
    • fullPath: 目录操作对象的完整路径,文件系统的绝对路径
    • fileSystem: 文件操作对象所属的文件系统对象,参考FileSystem

方法:

 

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: 文件写入操作错误代码

方法:

    • abort: 终止文件写入操作
    • seek: 定位文件操作位置
    • truncate: 按照指定长度截断文件
    • write: 向文件中写入数据

事件:

    • 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 ! 打完收工