Chrome 86默认启用Native File System API

2020-09-09 11:11:11

Native File System API是Chrome的原始试用版,是Capability项目的一部分。这篇帖子将随着实施的进展而更新。有关最新更新,请参阅What';s new部分。

原生文件系统API(以前称为可写文件API)使开发人员能够构建功能强大的Web应用程序,这些应用程序可以与用户本地设备上的文件交互,如IDE、照片和视频编辑器、文本编辑器等等。在用户授予Web应用程序访问权限后,此API允许Web应用程序直接读取或保存对文件的更改,并在用户的设备上进行切换。除了读写文件之外,Native File SystemAPI还提供打开目录并枚举其内容的功能。

如果您以前使用过读写文件,那么我将要与您分享的大部分内容对您来说都是熟悉的。不管怎样,我鼓励你阅读,因为没有一个系统是相似的。

注意:我们在NativeFile system API的设计和实现上花了很多心思,以确保人们可以轻松地管理他们的文件。有关详细信息,请参阅安全和权限部分。

为了展示Native File System API的真正功能和用处,我编写了一个单文件文本编辑器。它允许您打开文本文件、编辑文本文件、将更改保存回磁盘,或启动新文件并将更改保存到磁盘。它不是什么花哨的东西,但足以帮助你理解概念。

如果您想在本地试验Native File system API,请在Chrome://flag中启用#ative-file-system-api标志。

从Chrome83开始,针对所有桌面平台的Native filesystem API开始了新的原始试用版。

如果您有第一个原始试用(从Chrome 78到Chrome 82运行)的原始试用令牌,则需要获取新的原始试用令牌。

Origin试用版允许您尝试新功能,并就其可用性、实用性和有效性向Web标准社区提供反馈。有关详细信息,请参阅Web开发人员原版试用指南。若要注册此原版试用版或其他原版试用版,请访问注册页。

将令牌添加到您的页面。有两种方法可以做到这一点:在每个页面的头部添加一个Origin-Trial<;meta>;标记。例如,这可能类似于:<;meta http-equv=";Origin-Trial";Content=";TOKEN_GOES_HERE";>;

如果您可以配置您的服务器,您还可以使用原始试用HTTP标头添加令牌。生成的响应头应该类似于:Origin-Trial:Token_Goes_Here。

我要处理的第一个用例是让用户选择一个文件,然后从磁盘打开并读取该文件。

Native File System API的入口点是window.chooseFileSystemEntries()。当被调用时,它会显示一个文件选取器对话框,并提示用户选择一个文件。选择文件后,API返回该文件的句柄。可选的选项参数允许您影响文件选取器的行为,例如,通过允许用户选择多个文件、目录或不同的文件类型。在没有指定任何选项的情况下,文件选取器允许用户选择单个文件。这非常适合我们的文本编辑器。

与许多其他功能强大的API一样,调用chooseFileSystemEntries()必须在安全上下文中完成,并且必须从用户手势调用。

让fileHandle;但是让OpenFile。AddEventListener(';单击';,异步(E)=>;{fileHandle=等待窗口。ChooseFileSystemEntries();//处理文件句柄});

一旦用户选择了一个文件,ChooseFileSystemEntries()将返回一个句柄,在本例中是一个包含与文件交互所需属性和方法的FileSystemFileHandle。

在周围保留对文件句柄的引用是很有帮助的,这样可以在以后使用。将更改保存回文件或执行任何其他文件操作都需要它。

现在您已经有了文件的句柄,您可以获取文件的属性,或者访问文件本身。现在,让我们简单地阅读它的内容。调用handle.getFile()返回一个File对象,该对象包含一个blob。要从BLOB获取数据,请调用其方法之一(Slice()、stream()、text()、arrayBuffer())。

只要磁盘上的底层文件没有更改,FileSystemFileHandle.getFile()返回的File对象就是只读的。如果修改了磁盘上的文件,则File对象将变得不可读,您将需要再次调用getFile()来获取新的File对象来读取更改的数据。

当用户单击“打开”按钮时,浏览器会显示一个文件选取器。一旦他们选择了一个文件,应用程序就会读取内容并将其放入文本区域。

让fileHandle;但是让OpenFile。AddEventListener(';单击';,异步(E)=>;{fileHandle=等待窗口。ChooseFileSystemEntries();const file=await fileHandle。GetFile();const content=等待文件。Text();textArea.value=内容;});

在文本编辑器中,有两种保存文件的方式:保存和另存为。保存只是使用我们之前获得的文件句柄将更改写回原始文件。但是另存为会创建一个新文件,因此需要一个新的文件句柄。

通过{type:';save-file';}选择FileSystemEntries()将在";save";模式下显示文件选取器,允许用户选择要用于保存的新文件。对于文本编辑器,我还希望它自动添加一个.txt扩展名,所以我提供了一些额外的参数。

异步函数getNewFileHandle(){const opts={type:';save-file';,接受:[{description:';Text file';,Extensions:[';txt';],mimeTypes:[';Text/Plain';],}],};Const Handle=等待窗口。ChooseFileSystemEntries(Opts);返回句柄;}

您可以在GitHub上的我的textteditor演示中找到保存对文件的更改的所有代码。核心文件系统交互位于fs-helpers.js中。在最简单的情况下,该过程看起来如下面的代码所示。我将详细介绍每一步并加以解释。

异步函数writeFile(fileHandle,Contents){//创建要写入的FileSystemWritableFileStream。常量可写=等待fileHandle。CreateWritable();//将文件内容写入流。等待可写。Write(Contents);//关闭文件并将内容写入磁盘。等待可写。Close();}。

将数据写入磁盘使用FileSystemWritableFileStream,本质上是WritableStream。通过对文件句柄对象调用createWritable()来创建流。当调用createWritable()时,Chrome首先检查用户是否授予了对文件的写权限。如果未授予写入权限,浏览器将提示用户授予权限。如果未授予权限,createWritable()将抛出DOMException,应用程序将无法写入文件。在文本编辑器中,这些DOMException在saveFile()方法中处理。

Write()方法接受一个字符串,这是我们在文本编辑器中需要的,但它也可以接受BufferSource或Blob。例如,您可以通过管道将流直接发送到它:

异步函数writeURLToFile(fileHandle,url){//创建要写入的FileSystemWritableFileStream。常量可写=等待fileHandle。CreateWritable();//对内容进行HTTP请求。Const response=aWait Fetch(Url);//将响应串流到文件中。等待回应。身体。PipeTo(Writable);//pipeTo()默认关闭目标管道,无需关闭。}。

您还可以在流中查找()或截断()以更新特定位置的文件,或调整文件大小。

注意:在关闭流之前(通过调用Close()或通过管道自动关闭流),不会将更改写入磁盘。

Chrome83增加了对可写流的支持,并删除了以前写入磁盘的方法。它暂时记录在下面,直到Chrome83稳定上市。

异步函数writeFile(fileHandle,Contents){//创建编写器(必要时请求权限)。常量编写器=等待fileHandle。CreateWriter();//写入待写内容的全长。Write(0,Contents);//关闭文件并将内容写入磁盘等待写入。Close();}。

要将数据写入磁盘,我需要一个FileSystemWriter。通过对文件句柄对象调用createWriter()来创建一个。调用createWriter()时,Chrome首先检查用户是否已授予文件的写入权限。如果未授予写入权限,浏览器将提示用户提供权限。如果未授予权限,createWriter()将抛出DOMException,应用程序将无法写入文件。在textteditor中,这些DOMException在saveFile()方法中处理。

调用FileSystemWriter.write()来写入内容。Write()方法接受一个字符串,这就是我们想要的文本编辑器。但它也可以采用BufferSource或Blob。最后,通过调用FileSystemWriter.close()完成编写。

从Chrome83开始,文件句柄是可序列化的,这意味着您可以将文件句柄保存到IndexedDB,或者在相同的顶层源之间对它们进行PostMessage。

将文件句柄保存到IndexedDB意味着您可以存储状态,或者记住用户正在处理哪些文件。这使得保存最近打开或编辑的文件的列表、提供在应用程序打开时重新打开最后一个文件等成为可能。在文本编辑器中,我存储了用户最近打开的5个文件的列表,以便再次访问这些文件。

由于不会在会话之间保持权限,因此您应该使用queryPermission()验证用户是否授予了对该文件的权限。如果他们没有,请使用requestPermission()请求权限。

在文本编辑器中,我创建了一个verifyPermission()方法,用于检查用户是否已授予权限,并在需要时发出请求。

异步函数verifyPermission(fileHandle,with Write){const opts={};if(With Write){opts.writable=true;}//检查我们是否已经有权限,如果有,返回true。IF(等待fileHandle。QueryPermission(Opts)=';已授予';){return true;}//请求权限,如果用户授予权限,则返回true。IF(等待fileHandle。RequestPermission(Opts)=';已授予';){return true;}//用户未授予权限,返回false。返回FALSE;}

通过使用读取请求请求写入权限,我减少了权限提示的数量,用户在打开文件时会看到一个提示,并授予读取和写入该文件的权限。

要枚举目录中的所有文件,请调用chooseFileSystemEntries(),并将type选项设置为';open-directory';。用户在选取器中选择一个目录,然后返回FileSystemDirectoryHandle.允许您枚举和访问目录的文件。

Const butDir=文档。GetElementById(';但是目录';);但是目录。AddEventListener(';单击';,异步(E)=>;{const opts={type:';open-directory';};const句柄=等待窗口。ChooseFileSystemEntries(Opts);const Entries=等待句柄。GetEntries();用于等待(条目的常量条目){Const Kind=条目.isFile?';文件';:';目录';控制台。Log(Kind,entry.name);}});

允许在IndexedDB中序列化和存储文件句柄,或通过postMessage()将其发送到同源中的其他窗口或工作程序。请注意,在浏览器会话之间不保留权限。例如,当abrowser选项卡重新打开,并且从IndexedDB获得文件句柄时,用户将需要再次授予读取和写入文件的权限。

添加对isSameEntry()的支持,如果两个条目表示相同的文件或目录,则返回true。

更新使用率指示器以指示用户是否已授予文件的域权限,包括新的只读指示器。

添加对Resolve()的支持,它将返回从一个条目到另一个条目的相对路径。这对于多文件编辑器特别有用,在这些编辑器中,您可能希望突出显示正在编辑的文件的父目录。

当授予对文件的读写权限时,该权限在所有同源选项卡之间共享,与其他Web平台API一致。

由于API还不能与所有浏览器兼容,我们提供了一个名为Browser-nativefs的库,它可以在任何可用的地方使用新的API,但在不可用的时候会退回到旧的方法。

Chrome团队使用在控制对强大Web平台功能的访问中定义的核心原则(包括用户控制和透明度以及用户人体工程学)设计并实现了Native File System API。

打开文件时,用户通过文件选取器提供读取文件或目录的权限。当从安全上下文提供服务时,打开文件选取器只能通过用户手势显示。如果用户改变主意,他们可以取消文件选取器中的选择,网站将无法访问任何内容。这与<;input type=";file";>;元素的行为相同。

类似地,当Web应用程序想要保存新文件时,浏览器将显示保存文件选择器,允许用户指定新文件的名称和位置。由于他们要将新文件保存到设备(版本覆盖现有文件),因此文件选取器会授予应用程序写入该文件的权限。

为了帮助保护用户及其数据,浏览器可能会限制用户保存到某些文件夹的能力,例如Windows、MacOS Library文件夹等核心操作系统文件夹。发生这种情况时,浏览器将显示模式提示并要求用户选择其他文件夹。

未经用户明确许可,Web应用程序无法修改磁盘上的文件。

如果用户想要保存对他们之前授予的访问权限的文件的更改,浏览器将显示模式权限提示,请求站点将更改写入磁盘的权限。权限请求只能由用户手势触发,例如,通过单击";保存";按钮。

或者,可以编辑多个文件的Web应用程序(如IDE)也可以在打开时请求保存更改的权限。

如果用户选择取消,并且不授予写入访问权限,则WebApp无法将更改保存到本地文件。它应该提供另一种方法来允许用户保存他们的数据,例如,通过提供下载文件、将数据保存到云等方式。

一旦用户授予web应用程序保存本地文件的权限,Chrome将在无所不包的框中显示一个图标。单击全能框图标将打开一个弹出窗口,显示用户已授予访问权限的文件列表。如果用户愿意,可以很容易地撤销该访问权限。

Web应用可以继续保存对文件所做的更改,而不会进行提示,直到该来源的所有选项卡都关闭为止。一旦选项卡关闭,网站将失去所有访问权限。用户下次使用Web应用程序时,系统将再次提示他们访问这些文件。

这个API有没有什么地方不像你预想的那样工作?或者是否缺少实现您所需的方法或属性?对安全模型有疑问或评论吗?

在WICG Native File System GitHub Repo上提交规范问题,或将您的想法添加到现有问题中。

你在Chrome的实现中发现错误了吗?或者实现与规范不同?

在https://new.crbug.com.上提交漏洞。请确保包含尽可能多的详细信息、简单的再现说明,并将组件设置为Blink>;Storage>;FileSystem(闪烁>;存储>;文件系统)。Glitchworks非常适合分享快速、轻松的批评。

计划在您的站点上使用本机文件系统API吗?您的公众支持帮助我们确定功能的优先顺序,并向其他浏览器供应商表明支持这些功能是多么重要。

向@ChromiumDev发送一条带有#nativefs的推文,让我们知道您在哪里使用它,以及您是如何使用它的。