笔者开源了一个Web思维导图mind-map,数据默认是存储在localstorage里,如果想保留到本地文件,须要应用导出性能,下次关上再应用导入性能,编辑完如果又想保留到文件,那么又须要从从新导出笼罩原来的文件,不得不说,能够但不优雅,所以最近减少了间接编辑本地文件的能力,体验了一下,还是不错的,并且就是调调API的事件,很简略,何乐而不为。

配角就是showOpenFilePicker和showSaveFilePicker两个API,笔者基于它俩开发了三个性能:

新建另存为其实一样的,只不过一个保留的是空数据,一个是以后的数据,当创立或关上文件胜利后,操作的时候数据会间接保留到本地文件里,不再须要进行手动的导出,这种体验其实就和本地编辑器没什么区别了。

关上

先来看看关上文件,调用的是showSaveFilePicker办法,返回一个Promise,抉择文件胜利了那么Promise的后果是一个数组,每一项代表一个文件的操作句柄:

如果要获取某个文件的内容或写入某个文件就须要通过这些文件句柄对象。如果没有抉择或抉择失败了Promise则会出错:

这个办法接管一个选项对象作为参数:

  • options.multiple

布尔值,设置是否能够抉择多个文件。

  • options.types

一个数组,设置容许被抉择的文件类型,数组每一项都是一个对象:

{    description: '',    accept: {        '': []    }}

description用于阐明,如同没什么用,accept是个对象,key为MIME type,value为一个数组,代表容许的文件扩展名。

如果MIME type设置的很具体,比方application/json,那么value不传的话只能抉择文件后缀为.json的文件,如果value设置了扩展名的话,则在默认的.json文件外还容许抉择设置的扩展名的文件,比方设置为['.smm'],那么.json.smm为后缀的文件都能够抉择:

如果MIME type设置的比拟宽泛的话,比方application/*,那么所有MIME typeapplication类型的文件都能够抉择,就算value只设置了一个.json,其余类型的文件也是能够抉择的,所以value的作用不是限度,而是裁减。

然而呢,这种限度能够轻松冲破,只有点击扩展名关上下拉列表抉择所有文件选项,那么还是想选什么文件就选什么文件,有敌人晓得怎么解决的欢送评论区留言。

  • options.excludeAcceptAllOption

布尔值,默认为false,即容许不配置types选项,反对抉择所有文件,如果设为true,那么types选项不能为空,必须要限度一种文件类型。

笔者的思维导图文件格式应用的是.json,并且吃饱了撑的本人定义了一个格局.smm,其实就是json,并且同一时间只能编辑一个文件,那么关上文件的代码如下所示:

let fileHandle = nullasync openLocalFile() {    try {        let [ _fileHandle ] = await window.showOpenFilePicker({            types: [                {                    description: '',                    accept: {                        'application/json': ['.smm']                    }                },            ],            excludeAcceptAllOption: true,            multiple: false        });        if (!_fileHandle) {            return        }        fileHandle = _fileHandle        if (fileHandle.kind === 'directory') {            this.$message.warning('请抉择文件')            return        }        this.readFile()    } catch (error) {        if (error.toString().includes('aborted')) {            return        }        this.$message.warning('你的浏览器可能不反对哦')    }}

将文件句柄保存起来,接下来都会基于它来操作文件,先来看看文件句柄对象,它存在两个办法:

  • getFile()

返回一个Promise,获取该句柄所对应的文件对象,其实就是咱们常见的File对象:

  • createWritable()

返回也是一个Promise,创立一个能够写入文件的文件流对象:

基于这两个办法咱们就能够读取关上文件的内容及把新内容写入文件:

// 读取文件async readFile() {    let file = await fileHandle.getFile();    let fileReader = new FileReader();    fileReader.onload = async () => {        // fileReader.result    }    fileReader.readAsText(file);}// 写入文件async writeLocalFile(content) {    if (!fileHandle) {        return;    }    let string = JSON.stringify(content);    const writable = await fileHandle.createWritable();    await writable.write(string);    await writable.close();}

页面内第一次调用createWritable办法浏览器会弹个窗询问用户是否容许:

每调用一次createWritable办法都会在你的本地创立一个.crswap文件:

相当于一个临时文件,没有调用写入流writableclose办法前,调用它的write办法写入的内容默认都保留在这个文件,只有调用close当前才会更新到源文件,并且主动删除这个临时文件,另外页面敞开,也会删除这些文件。

写入流默认是空的,每调用一次write办法,都会在.crswap中追加内容,然而能够指定写入的地位:

await writable.write({ type: "write", position: 0, data: string });

这样会从指定的字节数开始写入,留神是替换,而不是插入。

所以为了不便起见,最好还是创立、写入就敞开,再写再创立。

新建

新建调用的是showSaveFilePicker办法,也接管一个选项对象为参数,有两个选项和showOpenFilePicker办法是一样的,即typesexcludeAcceptAllOption,之外还有一个选项:

  • suggestedName

默认填充的文件名称,为空则创立文件时输入框就是空的。

能够间接输出文件名创立新文件,也能够点击曾经存在的文件进行替换。

创立胜利返回的也是一个文件句柄,那么创立文件就很简略了:

async createLocalFile(content) {    try {        let _fileHandle = await window.showSaveFilePicker({            types: [{                description: '',                accept: {'application/json': ['.smm']},            }],            suggestedName: '思维导图'        });        if (!_fileHandle) {            return;        }        const loading = this.$loading({            lock: true,            text: '正在创立文件',            spinner: 'el-icon-loading',            background: 'rgba(0, 0, 0, 0.7)'        });        fileHandle = _fileHandle;        await this.writeLocalFile(content);        await this.readFile();        loading.close();    } catch (error) {        if (error.toString().includes('aborted')) {            return        }        this.$message.warning('你的浏览器可能不反对哦');    }}

来看看实际效果:

总结

最初再来看看兼容性:

因为目前还是试验性质,所以能够看到是一片红,然而因为我的自身也只是一个示例我的项目,所以问题不大,有胜于无。

另外这个个性目前也只能在HTTPS协定或localhost下才可用,其余状况下window对象是不存在这两个API的,所以须要做好错误处理。