笔者开源了一个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 type
为application
类型的文件都能够抉择,就算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
文件:
相当于一个临时文件,没有调用写入流writable
的close
办法前,调用它的write
办法写入的内容默认都保留在这个文件,只有调用close
当前才会更新到源文件,并且主动删除这个临时文件,另外页面敞开,也会删除这些文件。
写入流默认是空的,每调用一次write
办法,都会在.crswap
中追加内容,然而能够指定写入的地位:
await writable.write({ type: "write", position: 0, data: string });
这样会从指定的字节数开始写入,留神是替换,而不是插入。
所以为了不便起见,最好还是创立、写入就敞开,再写再创立。
新建
新建调用的是showSaveFilePicker
办法,也接管一个选项对象为参数,有两个选项和showOpenFilePicker
办法是一样的,即types
和excludeAcceptAllOption
,之外还有一个选项:
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
的,所以须要做好错误处理。