应用程序与本地文件交互是非常常见的。例如,常规工作流是打开文件、进行一些更改并保存文件。对于网络应用来说,这可能很难实现。可以使用IndexedDB API、具有文件类型的HTML输入元素、具有下载属性的HTML锚元素等来模拟文件操作,但这需要对这些标准有很好的理解,并仔细设计以获得良好的用户体验。此外,对于频繁操作和大文件,性能可能不令人满意。
文件系统访问API使web应用程序能够轻松高效地访问文件。它提供了一种直接创建、打开、读取和写入文件的方法。它还允许应用程序创建目录并枚举其内容。
WebKit在origin private File System中增加了对文件系统访问API的支持,该文件系统是某个origin的专用存储端点。从概念上讲,每个源站都拥有一个独立的目录,页面只能访问其源站目录中的文件或目录。例如https://webkit.org无法读取由创建的文件https://apple.com.
基于不同浏览器的实现,源私有文件系统中的一个条目不一定映射到用户本地文件系统中的一个条目——它可以是存储在某个数据库中的对象。这意味着通过文件系统访问API创建的文件或目录可能无法从浏览器外部轻松检索。
该API目前不适用于处于私人浏览模式的Safari windows。对于可用的位置,其存储寿命与IndexedDB和LocalStorage等其他持久存储类型相同。存储策略将符合存储标准。Safari用户可以通过macOS上的首选项或iOS上的设置查看和删除站点的文件系统存储。
源私有文件系统的文件系统访问API在r284131的WebKit中启用。在Safari网站上提供:
在macOS 12.4和iOS 15.4上的Safari中,我们介绍了FileSystemFileHandle的getFile()方法。
FileSystemHandle,表示文件系统中的一个条目。它有工人和工人两种
FileSystemSyncAccessHandle,它提供一个专用的双工流,用于同步读写条目。与上面的界面不同,FileSystemSyncAccessHandle既存在于窗口上下文中,也存在于工作上下文中,FileSystemSyncAccessHandle仅在工作上下文中可用。
考虑到这些基本接口,让我们通过一些示例来了解如何使用它们。
在源私有文件系统中,FileSystemHandle表示源空间的根目录或根目录的后代。因此,第一步是获取根文件系统DirectoryHandle。这是通过StorageManager界面完成的。
使用像root这样的FileSystemDirectoryHandle对象,您可以使用getDirectoryHandle()和getFileHandle()方法以特定的名称访问其子对象。
//创建一个名为Untiled的文件。根目录下的txt。const untitledFile=等待根。getFileHandle(";Untitled.txt";,{";create";:true})//访问现有的无标题文件。txt文件。//untitledFile和existingUntitledFile指向同一条目。const existingUntitledFile=等待根。getFileHandle(";Untitled.txt";)//创建一个名为“日记文件夹”的目录。const diaryDirectory=wait root。getDirectoryHandle(";日记文件夹";,{";创建";:true});
要在FileSystemHandle表示的文件或目录中移动,可以使用move()方法。第一个参数是代表目标父目录的FileSystemDirectoryHandle,第二个参数是代表目标文件名的USVString。字符串必须是有效的文件名。
//移动无标题。txt从/root/到/root/Diary文件夹/。等待untitledFile。移动(日记目录,untitledFile.name);//改名为Untitled。txt至2001年2月。txt等待untitledFile。move(日记目录,";Feb#u 01.txt";)//以上两个步骤可以组合为://wait untitledFile。move(日记目录,";Feb#u 01.txt";);
要确定FileSystemHandle是否是现有FileSystemDirectoryHandle的后代,并获取它们的相对路径,可以使用resolve()方法。结果是形成路径的组件名称数组。
//访问2月1日。日志文件夹中的txt。const diaryFile=wait diaryDirectory。getFileHandle(";Feb_01.txt";)//解析2月1日之间的路径。txt和root。const relativePath=等待根。解析(日记文件);//relativePath是[";日记文件夹";,";Feb#u 01.txt";]。
上面介绍的方法要求您知道目标的名称,但是如果您不知道名称,您仍然可以通过使用keys()、values()和entries()方法返回的异步迭代器枚举现有目录的内容来获得它。
//在根目录下创建一个名为Trash的目录。const trashDirectory=wait root。getDirectoryHandle(";垃圾";,{";创建";:true});//在root/下查找目录并打印它们的名称。const directoryNames=[];for await(root.values()的常量句柄){if(handle.kind==";directory";){directoryNames.push(handle.name);}}//目录名是[";垃圾和#34;";日记文件夹和#34;]。
对于FileSystemDirectoryHandle对象,可以使用removentry()方法按名称删除其子项。
//删除2月1日。日志文件夹中的txt。等待日记目录。removentry(diaryFile.name);//删除垃圾及其所有后代。等待根。removeEntry(trashDirectory.name,{";recursive";:true});
一旦有了表示目标文件的FileSystemFileHandle,就可以通过使用getFile()方法将其转换为文件对象来读取其属性和内容。您可以使用文件的接口获取文件信息和内容。
读取文件的另一种方法是使用FileSystemSyncAccessHandle接口的read()方法。可以使用createSyncAccessHandle()方法从FileSystemFileHandle对象创建FileSystemSyncAccessHandle。由于FileSystemSyncAccessHandle仅在工作上下文中可用,因此需要先创建一个专用的工作上下文。
与返回承诺的getFile()不同,read()是同步的,因此提供了更好的性能。如果您的目标是实现最高效的文件访问,那么FileSystemSyncAccessHandle就是最佳选择。
要编写文件,可以使用FileSystemSyncAccessHandle的synchronous write()方法。在当前的实现中,这是在WebKit中编写文件的唯一方法。
要实现同步读写操作,FileSystemSyncAccessHandle必须以独占方式访问文件条目。因此,如果前一个FileSystemSyncAccessHandle未正确关闭,则尝试在条目上创建第二个FileSystemSyncAccessHandle将失败。
//获取现有草稿。txt文件。const root=等待导航器。存储getDirectory();const draftFile=wait root。getFileHandle(";Draft.txt";)//在文件上创建FileSystemSyncAccessHandle。const accessHandle=wait draftFile。createSyncAccessHandle();//获取文件的大小。const fileSize=wait accessHandle。getSize();//将文件内容读取到缓冲区。const readBuffer=new ArrayBuffer(文件大小);const readSize=accessHandle。read(readBuffer,{";at";:0});//在文件末尾写一个句子。const encoder=new textcoder();const writeBuffer=编码器。encode(";感谢您阅读本文。";);const writeSize=accessHandle。write(writeBuffer,{";at";:readSize});//将文件截断为1字节。等待访问句柄。截断(1);//将更改保存到磁盘。等待访问句柄。flush();//如果完成,请始终关闭FileSystemSyncAccessHandle。等待访问句柄。close();
如果你的web应用需要与文件交互,你应该尝试新的文件系统访问API。它提供了类似于本机文件系统API的接口,具有优化的性能。
随着标准的发展和发展,我们将根据文件系统访问API规范不断添加或更新接口和方法。如果您在使用此API时遇到任何问题,请在bug上提交bug。webkit。“网站存储”组件下的组织。您还可以为功能请求创建一个新的bug报告,描述您的用例以及该功能的重要性。如果您对API本身有任何疑问或建议,可以在WICG repo中提交规范问题。你的反馈对我们非常重要。