上一篇文章提到了,最近做一个基于 File System/IndexedDB的应用,上一篇是定额和使用的查询。

因为LocalFileSystem只有chrome支持,有点尴尬,如果按需加载又何来尴尬。

这一篇是关于文件和目录的操作的,怕陷入回调陷阱,基于promise和ES7的await。

首先介绍两个函数:

第一个是 :toPromise,把那种带成功失败回调的函数转化为Promise,因为File API的File System里面很多方法都是这种格式的。
/**
* 转为promise,主要是把 a.b(param1,param2,successCallback,errorCall) 转为promise
* @param {*期待的是函数} obj
* @param {*上下文} ctx
* @param {*参数} args
*/
function toPromise(obj, ctx = window, ...args) {
if (!obj) return obj //如果已经是Promise对象
if ('function' == typeof obj.then) return obj //若obj是函数直接转换
if ('function' == typeof obj) return _toPromise(obj) return obj; //函数转成 promise
function _toPromise(fn) {
return new Promise((resolve, reject) => { fn.call(ctx, ...args, (...ags) => {
//多个参数返回数组,单个直接返回对象
resolve(ags && ags.length > 1 ? ags : ags[0] || null)
}, (err) => {
reject(err)
}) })
}
}

 第二个是 promiseForEach,顺序的执行多个Promise,思想也就是then的拼接

/**
* https://segmentfault.com/q/1010000007499416
* Promise for forEach
* @param {*数组} arr
* @param {*回调} cb(val)返回的应该是Promise
* @param {*是否需要执行结果集} needResults
*/
function promiseForEach(arr, cb, needResults) {
let realResult = [], lastResult //lastResult参数暂无用
let result = Promise.resolve()
Array.from(arr).forEach((val, index) => {
result = result.then(() => {
return cb(val, index).then((res) => {
lastResult = res
needResults && realResult.push(res)
})
})
}) return needResults ? result.then(() => realResult) : result
}

 

这两个方法完毕后,就直接上主体代码了, hold on。

/**
* 参考的API:
* http://w3c.github.io/quota-api/
*
*/ if (!window.location.origin) {
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
}
//文件系统请求标识
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem
//根据URL取得文件的读取权限
window.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL //临时储存和永久存储
navigator.temporaryStorage = navigator.temporaryStorage || navigator.webkitTemporaryStorage;
navigator.persistentStorage = navigator.persistentStorage || navigator.webkitPersistentStorage; //常量
const _TEMPORARY = 'temporary',
_PERSISTENT = 'persistent',
FS_SCHEME = 'filesystem:' class LocalFileSystem { constructor(fs) {
this._fs = fs //文件系统
this._root = fs.root //文件系统的根Entry
this._instance = null //示例对象
this._type = null //类型,window.TEMPORAR| window.PERSISTENT
this._fsBaseUrl = null //文件系统的基础地址
} /**
*
* @param {* window.TEMPORAR(0) |window.PERSISTENT(1)} type
* @param {* 申请空间大小,单位为M } size
*/
static getInstance(type = window.TEMPORARY, size = 1) { if (this._instance) {
return Promise.resolve(this._instance)
}
//类型
let typeValue = type,
//文件系统基础地址
fsBaseUrl = FS_SCHEME + location.origin + '/' + (type == 1 ? _PERSISTENT : _TEMPORARY) + '/'
return new Promise((resolve, reject) => {
window.requestFileSystem(type, size * 1024 * 1024, fs => {
this._instance = new LocalFileSystem(fs)
this._instance._type = typeValue;
this._instance._fsBaseUrl = fsBaseUrl
return resolve(this._instance)
}, (err) => reject(err))
}) } /**
* 获得FileEntry
* @param {*文件路径} path
*/
_getFileEntry(path, create = false) {
return toPromise(this._root.getFile, this._root, path, { create, exclusive: false })
} /**
* 获取目录
* @param {*路径} path
* @param {*不存在的时候是否创建} create
*/
_getDirectory(path = '', create = false) {
return toPromise(this._root.getDirectory, this._root, path, { create, exclusive: false })
} async _readEntriesRecursively(rootEntry, refResults) { if (rootEntry.isFile) {
return Promise.resolve(rootEntry)
}
let reader = rootEntry.createReader()
let entries = await toPromise(reader.readEntries, reader)
refResults.push(...entries)
let psEntries = entries.map(entry => this._readEntriesRecursively(entry, refResults))
return Promise.all(psEntries)
} /**
* 获得Entry
* @param {*路径} path
*/
resolveLocalFileSystemURL(path) {
return toPromise(window.resolveLocalFileSystemURL, window, `${this._fsBaseUrl}${path.startsWith('\/') ? path.substr(1) : path}`)
} /**
* 获得文件
* @param {*文件路径} path
*/
async getFile(path) {
let fe = await this._getFileEntry(path)
return toPromise(fe.file, fe)
} /**
* 往文件写入内容
* @param {*文件路径} path
* @param {*写入的内容} content
* @param {*数据类型} type
* @param {*是否是append} append
*/
async writeToFile(path, content, type = 'text/plain', append = false) { let fe = await this._getFileEntry(path, true)
let writer = await toPromise(fe.createWriter, fe);
let data = content; //不是blob,转为blob
if (content instanceof ArrayBuffer) {
data = new Blob([new Uint8Array(content)], { type })
} else if (typeof content == 'string') {
data = new Blob([content], { type: 'text/plain' })
} else {
data = new Blob([content])
} if (append) {
writer.seek(writer.length)
} return new Promise((resolve, reject) => {
//写入成功
writer.onwriteend = () => {
resolve(true)
} //写入失败
writer.onerror = (err) => {
reject(err)
} writer.write(data)
})
} /**
* 获取指定目录下的文件和文件夹
* @param {*路径} path
*/
async readEntries(path = '') {
let entry = null
if (!path) {
entry = this._root
} else {
entry = await this.resolveLocalFileSystemURL(path)
}
let reader = entry.createReader()
return toPromise(reader.readEntries, reader);
} /**
* 获取所有的文件和文件夹,按照路径排序
*/
async readAllEntries() {
let refResults = []
let entries = await this._readEntriesRecursively(this._root, refResults)
refResults.sort((a, b) => a.fullPath > b.fullPath)
return refResults } /**
* 确认目录存在,递归检查,没有会自动创建
* @param {*} directory
*/
async ensureDirectory(directory = '') {
//过滤空的目录,比如 '/music/' => ['','music','']
let _dirs = directory.split('/').filter(v => !!v) if (!_dirs || _dirs.length == 0) {
return Promise.resolve(true)
} return promiseForEach(_dirs, (dir, index) => {
return this._getDirectory(_dirs.slice(0, index + 1).join('/'), true)
}, true).then((rs) => {
console.log(rs)
return true
})
} /**
* 清除所有的文件和文件夹
*/
async clear() {
let entries = await this.readEntries()
let ps_entries = entries.map(e => e.isFile ? toPromise(e.remove, e) : toPromise(e.removeRecursively, e))
return Promise.all(ps_entries)
} /**
* Promise里面的错误处理
* @param {*reject}
*/
errorHandler(reject) {
return (error) => {
reject(error)
}
} } // 测试语句
//读取某个目录的子目录和文件: LocalFileSystem.getInstance().then(fs=>fs.readEntries()).then(f=>console.log(f))
//写文件 LocalFileSystem.getInstance().then(fs=>fs.writeToFile('music/txt.txt','爱死你')).then(f=>console.log(f))
//获取文件: LocalFileSystem.getInstance().then(fs=>fs.getFile('music/txt.txt')).then(f=>console.log(f))
//递归创建目录: LocalFileSystem.getInstance().then(fs=>fs.ensureDirectory('music/vbox')).then(r=>console.log('r:' + r))
//递归获取: LocalFileSystem.getInstance().then(fs=>fs.readAllEntries()).then(f=>console.log(f))
//删除所有: LocalFileSystem.getInstance().then(fs=>fs.clear()).then(f=>console.log(f)).catch(err=>console.log(err))

  

当然测试语句也在上面了,因为用了 await,那么大家自然知道了。需要 chrome://flags开启javascript的特性。

如果你有兴趣,代码地址:https://github.com/xiangwenhu/BlogCodes/tree/master/client/FileSystem,下载下来

npm install 之后, node server/app.js就可以查询demo了

 
 
参考:

File System 之本地文件系统的更多相关文章

  1. [LeetCode] Design In-Memory File System 设计内存文件系统

    Design an in-memory file system to simulate the following functions: ls: Given a path in string form ...

  2. chattr lsattr linux file system attributes - linux 文件系统扩展属性

    我们使用 linux 文件系统扩展属性,能够对linux文件系统进行进一步保护:从而给文件 赋予一些额外的限制:在有些情况下,能够对我们的系统提供保护: chattr命令用来改变文件属性.这项指令可改 ...

  3. GFS(Google File System,谷歌文件系统)----(1)文件系统简介

    分布式文件系统 系统是构建在普通的.廉价的机器上,因此故障是常态而不是意外 系统希望存储的是大量的大型文件(单个文件size很大) 系统支持两种类型读操作:大量的顺序读取以及小规模的随机读取(larg ...

  4. GFS(Google File System,谷歌文件系统)----(1)读写一致性

    GFS副本控制协议--中心化副本控制协议 对于副本集的更新操作有一个中心节点来协调管理,将分布式的并发操作转化为单点的并发操作,从而保证副本集内各节点的一致性.在GFS中,中心节点称之为Primary ...

  5. HDFS(Hadoop Distributed File System )

    HDFS(Hadoop Distributed File System ) HDFS(Hadoop Distributed File System )Hadoop分布式文件系统.是根据google发表 ...

  6. Fast File System

    不扯淡了,直接来写吧,一天一共要写三篇博客,还有两篇呢. 1. 这篇博客讲什么? Fast File System(FFS)快速文件系统,基本思想已经在在上一篇博客File System Implem ...

  7. File System Implementation 文件系统设计实现

    先来扯淡吧,上一篇文章说到要补习的第二篇文章介绍文件系统的,现在就来写吧.其实这些技术都已经是很久以前的了,但是不管怎么样,是基础,慢慢来学习吧.有种直接上Spark源码的冲动.. 1. 这篇博客具体 ...

  8. HTML5之本地文件系统API - File System API

    HTML5之本地文件系统API - File System API 新的HTML5标准给我们带来了大量的新特性和惊喜,例如,画图的画布Canvas,多媒体的audio和video等等.除了上面我们提到 ...

  9. HDFS(Hadoop Distributed File System )hadoop分布式文件系统。

    HDFS(Hadoop Distributed File System )hadoop分布式文件系统.HDFS有如下特点:保存多个副本,且提供容错机制,副本丢失或宕机自动恢复.默认存3份.运行在廉价的 ...

随机推荐

  1. Solr7使用Oracle数据源导入+中文分词

    安装目录假设为#solr_home,本文的#solr_home为apps/svr/solr 1. 在#solr_home/server/solr下新建文件夹,假设为mjd 2. 将#solr_home ...

  2. Create-React-App创建antd-mobile开发环境(学习中的记录)

    (参考别人结合自己的整理得出,若有错误请大神指出) Facebook 官方推出Create-React-App脚手架,基本可以零配置搭建基于webpack的React开发环境,内置了热更新等功能. 详 ...

  3. SOJ--Zig-Zag

    Zig-Zag 在图形图像处理中经常须要将一个二维的图像矩阵转化为一维的向量.二维化一维的过程实际上就是将二维数组的元素按某种顺序构成一维数组. 一种经常使用的序列叫"Zig-Zag&quo ...

  4. Hibernate学习笔记(五) — 多对多关系映射

    多对多关系映射 多对多建立关系相当于在第三张表中插入一行数据 多对多解除关系相当于在第三张表中删除一行数据 多对多改动关系相当于在第三张表中先删除后添加 多对多谁维护效率都一样.看需求 在实际开发过程 ...

  5. Android音频: 怎样使用AudioTrack播放一个WAV格式文件?

    翻译 By Long Luo 原文链接:Android Audio: Play a WAV file on an AudioTrack 译者注: 1. 因为这是技术文章,所以有些词句使用原文,表达更准 ...

  6. ajax_get/post_两级联动

    使用ajax实现菜单联动 通常情况下,GET请求用于从服务器上获取数据,POST请求用于向服务器发送数据. 需求:选择第一个下拉框的值,根据第一个下拉框的值显示第二个下拉框的值 首先使用GET方式. ...

  7. Photoshop 无法打开某些JPEG文件的成因

    最近想把QQ相册里的一些照片整理修复一下,所以下载了一些到本地,然后用PS CC 2018打开,结果PS告诉我"无法完成请求,因为程序错误".试了Win10系统自带的看图软件,能够 ...

  8. Python 3.6.3 利用Dlib 19.7库进行人脸识别

    0.引言 自己在下载dlib官网给的example代码时,一开始不知道怎么使用,在一番摸索之后弄明白怎么使用了: 现分享下 face_detector.py 和 face_landmark_detec ...

  9. 插入光盘,创建挂载点,挂载设备,安装rpm包,升级rpm包,卸载rpm包,查询rpm包是否安装,查询rpm包信息、安装位置,查询系统文件名属于哪个安装包

    插入光盘: 创建挂载点: 创建挂载点. 挂载设备:或者mount /dev/sr0 /mnt/cdrom 安装rpm包: [root@localhost Packages]# rpm -ivh mys ...

  10. springboot swagger-ui结合

    随着移动互联的发展,前后端的分离已经是趋势.前后端已不是传统部门的划分,而是它们各有一套的生态系统,包括不同的开发语言.不同的开发流程.构建方式.测试流程等.做前端的不需要会maven作为构建工具,后 ...