File System 之本地文件系统

小编 2026-06-05 阅读:111 评论:0
上一篇文章提到了,最近做一个基于 File System/IndexedDB的应用,上一篇是定额...

上一篇文章提到了,最近做一个基于 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了

 
 
参考:
using-localfilesystem
Promise循环串行执行写法
filer
版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表