1. 介绍
本节为非规范性内容。
该API使开发者能够构建与用户设备上其他(非Web)应用通过设备文件系统交互的强大应用程序。用户预期此功能的典型应用包括IDE、照片和视频编辑器、文本编辑器等。 用户授权Web应用访问后,API允许应用直接读取或保存对用户设备上文件和文件夹的更改。除了读写文件外,此API还提供打开目录并枚举其内容的能力。此外,Web应用还可以使用该API保存已获得访问权限的文件和目录引用,从而实现后续无需用户重新选择即可恢复访问同一内容。
该API类似于
<input type=file>
和
<input type=file webkitdirectory>
[entries-api]
,即用户通过文件和目录选择器对话框进行交互。与这些API不同,当前API完全是JavaScript API,不与表单和/或input元素集成。
该API是对[FS]中的API的扩展,其中规范了网站无需预先提示用户即可访问的桶式文件系统。
2. 文件与目录
2.1. 基本概念
合法的后缀码点是一个码点,其为ASCII字母数字, U+002B(+),或U+002E(.)。
注意:这些码点的选择是为了支持大多数已有的文件格式。绝大多数文件扩展名仅包含字母和数字,但复合扩展名(如
.tar.gz)以及如.c++(C++源代码)的扩展名也较为常见,因此允许+和.作为合规的码点。
2.2. 权限
"file-system" 强特性的权限相关算法和类型定义如下:
- 权限描述符类型
-
FileSystemPermissionDescriptor,定义如下:enum {FileSystemPermissionMode ,"read" };"readwrite" dictionary :FileSystemPermissionDescriptor PermissionDescriptor {required FileSystemHandle ;handle FileSystemPermissionMode = "read"; };mode - 权限状态约束
-
对于
FileSystemPermissionDescriptordesc,确定权限状态约束时,执行以下步骤:
让这些检查不再与条目关联。[whatwg/fs Issue #101]
- 权限请求算法
-
给定
FileSystemPermissionDescriptordesc和PermissionStatusstatus,执行以下步骤:-
在desc和status上运行默认权限查询算法。
-
令global为settings的全局对象。
-
如果global不是
Window, 则 抛出"SecurityError"DOMException。 -
如果global没有临时激活,则 抛出"
SecurityError"DOMException。 -
如果settings的origin 与settings的顶级origin不是同源,则 抛出"
SecurityError"DOMException。 -
请求使用权限 desc。
-
在desc和status上运行默认权限查询算法。
最好这个用户激活要求由上游定义。[WICG/permissions-request Issue #2]
-
FileSystemHandle
handle和FileSystemPermissionMode
mode,请执行以下步骤:
-
设置desc["
name"] 为 "file-system"。 -
设置desc["
handle"] 为handle。 -
设置desc["
mode"] 为mode。 -
返回desc的权限状态。
FileSystemHandle
handle和FileSystemPermissionMode
mode,请执行以下步骤:
-
设置desc["
name"] 为 "file-system"。 -
设置desc["
handle"] 为handle。 -
设置desc["
mode"] 为mode。 -
令status为对desc运行创建PermissionStatus的结果。
-
针对"
file-system" 特性,使用desc和status运行权限请求算法。 -
返回desc的权限状态。
当前FileSystemPermissionMode
只能为
"read"
或"readwrite"。
未来我们可能还会添加"write"模式,以支持仅写句柄。[Issue #119]
2.3. FileSystemHandle
接口
dictionary {FileSystemHandlePermissionDescriptor FileSystemPermissionMode = "read"; }; [mode Exposed =(Window ,Worker ),SecureContext ,Serializable ]partial interface FileSystemHandle {Promise <PermissionState >queryPermission (optional FileSystemHandlePermissionDescriptor = {});descriptor Promise <PermissionState >requestPermission (optional FileSystemHandlePermissionDescriptor = {}); };descriptor
2.3.1. queryPermission()
方法
- status = await handle .
queryPermission({mode: "read" })- status = await handle .
queryPermission()- status = (await navigator.
permissions.query({name: "file-system",handle: handle })).state - status = await handle .
-
查询此句柄读取权限的当前状态。 如果返回"
prompt" ,网站将不得不调用requestPermission()才能在此 句柄上执行任何操作。 如果返回"denied" ,任何操作都将拒绝。通常,由本地文件系统句柄工厂返回的句柄将 最初为其读取权限 状态返回"
granted" ,然而,除非通过用户撤销权限,否则从IndexedDB检索的句柄也很可能返回 "prompt"。 - status = await handle .
queryPermission({mode: "readwrite" })- status = (await navigator.
permissions.query({name: "file-system",handle: handle,mode: "readwrite" })).state - status = (await navigator.
-
查询此句柄写入权限的当前状态。 如果返回"
prompt", 尝试修改此句柄代表的 文件或目录将需要用户激活 并将向用户显示确认提示。 但是,如果此句柄读取权限的状态也是 "prompt" ,网站将需要调用requestPermission()。 对于读取访问,没有自动提示。
与权限API的query()
方法的集成尚未在Chrome中实现。
queryPermission(descriptor)
方法被调用时,必须运行以下步骤:
2.3.2. requestPermission()
方法
- status = await handle .
requestPermission({mode: "read" })- status = await handle .
requestPermission() - status = await handle .
-
如果此句柄读取权限的状态不是 "
prompt", 这将直接返回该状态。 如果是"prompt" 但是,需要用户激活, 这将向用户显示确认提示。 然后,根据用户对提示的响应,返回新的读取权限状态。 - status = await handle .
requestPermission({mode: "readwrite" }) -
如果此句柄写入权限的状态不是 "
prompt", 这将直接返回该状态。 如果此句柄读取权限的状态是 "denied" 这将返回该状态。否则,写入权限的状态是"
prompt" ,这将向用户显示确认提示。 然后,根据用户选择的内容,返回新的写入权限状态。
requestPermission(descriptor)
方法被调用时,必须运行以下步骤:
3. 访问本地文件系统
enum WellKnownDirectory {"desktop" ,"documents" ,"downloads" ,"music" ,"pictures" ,"videos" , };typedef (WellKnownDirectory or FileSystemHandle );StartInDirectory dictionary {FilePickerAcceptType USVString = "";description record <USVString , (USVString or sequence <USVString >)>; };accept dictionary {FilePickerOptions sequence <FilePickerAcceptType >;types boolean =excludeAcceptAllOption false ;DOMString ;id StartInDirectory ; };startIn dictionary :OpenFilePickerOptions FilePickerOptions {boolean =multiple false ; };dictionary :SaveFilePickerOptions FilePickerOptions {USVString ?; };suggestedName dictionary {DirectoryPickerOptions DOMString ;id StartInDirectory ;startIn FileSystemPermissionMode = "read"; }; [mode SecureContext ]partial interface Window {Promise <sequence <FileSystemFileHandle >>showOpenFilePicker (optional OpenFilePickerOptions = {});options Promise <FileSystemFileHandle >showSaveFilePicker (optional SaveFilePickerOptions = {});options Promise <FileSystemDirectoryHandle >showDirectoryPicker (optional DirectoryPickerOptions = {}); };options
showOpenFilePicker()、
showSaveFilePicker()
和showDirectoryPicker()
方法
统称为本地文件系统句柄工厂。
注意: 此规范中称为"本地文件系统"的内容,并不一定 严格指本地设备的文件系统。 我们所谓的本地文件系统 也可以由云提供商支持。例如,在Chrome OS上,这些 文件选择器还将允许您选择Google Drive上的文件和目录。
在Chrome版本早于85时,这是作为通用
chooseFileSystemEntries方法实现的。
3.1. 本地文件系统权限
用户在提示中选择本地文件
系统句柄工厂返回的特定文件这一事实
应该被用户代理视为用户打算授予网站对返回文件的读取访问权限
。因此,在本地文件
系统句柄工厂
之一返回的承诺解决时,权限状态 对于设置为返回句柄的描述符,
和handle
设置为返回句柄,
和mode
设置为"read"
应该是"granted"。
此外,对于showSaveFilePicker
调用,权限状态 对于设置为返回句柄的描述符,
和handle
设置为返回句柄,
和mode
设置为"readwrite"
应该是"granted"。
-
如果environment的起源是不透明起源, 返回以拒绝承诺 "
SecurityError"DOMException。 -
如果environment的起源与 environment的顶级起源不是同源, 返回以拒绝承诺 "
SecurityError"DOMException。 -
让global成为environment的全局对象。
-
如果global没有瞬时激活,则 抛出 "
SecurityError"DOMException。
3.2. 文件选择器选项
3.2.1. 接受的文件类型
showOpenFilePicker(options)
和 showSaveFilePicker(options)
方法接受一个
FilePickerOptions
参数,该参数允许网站指定文件选择器将让用户选择的文件类型。
types
中的每个条目指定文件选择器中显示的文件过滤器的单个用户可选选项。
每个选项由一个可选description
和一些MIME类型和扩展组成(指定为MIME类型到扩展列表的映射)。如果未提供描述,将生成一个描述。
扩展必须是以"."开头的字符串,并且仅包含有效后缀码点。
此外,扩展限制为16个码点的长度。
除了完整的MIME类型外,"*"可以用作MIME类型的子类型,以匹配例如所有图像格式的"image/*"。
网站应该始终为每个选项提供MIME类型和文件 扩展。在仅使用文件扩展来描述文件类型的平台上 用户代理可以匹配扩展,而在不使用扩展的平台上, 用户代理可以匹配MIME类型。
默认情况下,文件选择器还将包括一个选项,不应用任何过滤器,
让用户选择任何文件。将excludeAcceptAllOption
设置为true以
不在文件选择器中包含此选项。
例如,以下选项将让用户从三个不同的过滤器中选择一个。 一个用于文本文件(纯文本或HTML),一个用于图像,第三个不应用 任何过滤器,让用户选择任何文件。
const options= { types: [ { description: 'Text Files' , accept: { 'text/plain' : [ '.txt' , '.text' ], 'text/html' : [ '.html' , '.htm' ] } }, { description: 'Images' , accept: { 'image/*' : [ '.png' , '.gif' , '.jpeg' , '.jpg' ] } } ], };
另一方面,以下示例将只让用户选择SVG文件。对话框 将不显示不应用任何过滤器的选项。
const options= { types: [ { accept: { 'image/svg+xml' : '.svg' } }, ], excludeAcceptAllOption: true };
FilePickerOptions
options,
运行以下步骤:
-
-
让filter成为这些步骤,给定一个filename(一个字符串)和一个type(一个MIME 类型):
-
让parsedType成为使用解析MIME类型 的typeString的结果。
-
返回
false。
-
让description成为type["
description"]。 -
如果description是一个空字符串, 将description设置为一些用户可理解的字符串,描述 filter。
-
将(description, filter)追加到 accepts options。
-
如果accepts options是空的, 或者options["
excludeAcceptAllOption"] 是false:-
让description成为描述"all files"的用户可理解字符串。
-
让filter成为返回
true的算法。 -
将(description, filter)追加到accepts options。
-
-
-
返回accepts options。
3.2.2. 起始目录
id
和startIn
字段来建议文件选择器打开的目录。
如果这两个选项都没有指定,用户代理会记住上次选择文件或目录的目录,新选择器将从该目录开始。如果指定了id
,用户代理可以为不同的ID记住不同的目录
(用户代理只会对有限数量的ID记住目录)。
// 如果此ID存在映射到之前选择的目录,则从该目录开始。 // 否则,将为此ID创建从此文件选择器调用结果的目录映射。 const options= { id: 'foo' , };
将startIn
指定为FileSystemFileHandle
将导致对话框从该文件的父目录开始,而传递FileSystemDirectoryHandle
将导致对话框从传递的目录开始。这些优先于任何明确的id
也传递。
例如,给定一个FileSystemDirectoryHandle
project_dir,以下将显示一个文件选择器,从该目录开始:
// 选择器将打开到|project_dir|的目录,无论'foo'是否有有效映射。 const options= { id: 'foo' , startIn: | project_dir| , };
id
和startIn
字段只控制选择器打开的目录。在上面的示例中,不能假设
id
'foo'将映射到与project_dir
相同的目录
一旦文件选择器操作完成。
将startIn
指定为WellKnownDirectory
将导致对话框
从该目录开始,除非还传递了明确的id
并且该ID有映射到有效目录。
下面是一个同时指定id
和
startIn
为WellKnownDirectory
的示例。
如果给定ID存在映射到路径,则使用该映射。否则,使用通过WellKnownDirectory
建议的路径。
// 第一次指定ID 'foo'。它未映射到目录。 // 文件选择器将回退到打开到Downloads目录。TODO: link this. const options= { id: 'foo' , // 未映射。 startIn: " downloads " , // 从这里开始。 }; // 后来... // ID 'foo'可能或不可能被映射。例如,此ID的映射 // 可能已被逐出。 const options= { id: 'foo' , // 如果映射,则从这里开始。 startIn: " downloads " , // 否则,从这里开始。 };
startIn
和id
选项在Chrome 91中首次引入。
用户代理持有一个最近选择的目录映射,这是 一个映射 ,将起源映射到路径ID映射。
一个有效路径ID是一个字符串,其中每个字符都是ASCII字母数字或"_"或"-"。
为了防止路径ID映射无界增长,用户代理应该实现 某种机制来限制记住最近选择的目录的数量。这 可以通过例如逐出最近最少使用的条目来完成。 用户代理应该允许至少16个条目存储在路径ID映射中。
WellKnownDirectory枚举让网站从
几个已知目录中选择一个。各种值映射到的确切路径是实现定义的
(并且在某些情况下,这些可能甚至不代表磁盘上的实际路径)。
以下列表描述了每个值的含义,并给出了不同操作系统上可能的示例路径:
"desktop"-
用户的桌面目录,如果存在的话。例如这可能是
C:\Documents and Settings\username\Desktop,/Users/username/Desktop,或/home/username/Desktop。 "documents"-
用户创建的文档通常存储的目录。 例如
C:\Documents and Settings\username\My Documents,/Users/username/Documents,或/home/username/Documents。 "downloads"-
下载的文件通常存储的目录。 例如
C:\Documents and Settings\username\Downloads,/Users/username/Downloads,或/home/username/Downloads。 "music"-
音频文件通常存储的目录。 例如
C:\Documents and Settings\username\My Documents\My Music,/Users/username/Music,或/home/username/Music。 "pictures"-
用户静态图像通常存储的目录。 例如
C:\Documents and Settings\username\My Documents\My Pictures,/Users/username/Pictures,或/home/username/Pictures。 "videos"-
视频/电影通常存储的目录。 例如
C:\Documents and Settings\username\My Documents\My Videos,/Users/username/Movies,或/home/username/Videos。 -
让origin成为environment的起源。
-
如果startIn是一个
FileSystemHandle并且startIn不在 桶文件系统中: -
如果id非空:
-
如果最近选择的目录 映射[origin] 存在:
-
让path map成为最近选择的目录 映射[origin]。
-
如果path map[id] 存在,则返回path map[id]。
-
-
-
如果startIn是一个
WellKnownDirectory:-
返回对应于startIn的
WellKnownDirectory值的用户代理定义路径。
-
-
如果id未指定,或是空字符串:
-
如果最近选择的目录 映射[origin] 存在:
-
让path map成为最近选择的目录 映射[origin]。
-
如果path map[""] 存在,则返回path map[""]。
-
-
-
以用户代理特定方式返回默认路径。
-
让origin成为environment的起源。
-
如果最近选择的目录 映射[origin]不存在存在, 则设置最近选择的目录 映射[origin]为一个空的路径ID映射。
-
如果id未指定,让id成为空字符串。
-
设置最近选择的目录 映射[origin][id]为对应于entry的本地文件系统路径, 如果可以确定这样的路径。
- [ handle ] = await window .
showOpenFilePicker()- [ handle ] = await window .
showOpenFilePicker({multiple: false }) - [ handle ] = await window .
-
显示一个文件选择器,让用户选择单个现有文件,返回所选文件的句柄。
- handles = await window .
showOpenFilePicker({multiple: true }) -
显示一个文件选择器,让用户选择多个现有文件,返回所选文件的句柄。
可以向
showOpenFilePicker()传递额外选项 来指示网站想要用户选择的文件的类型和 文件选择器将打开的目录。请参见§ 3.2 文件选择器选项了解详情。 -
让accepts options成为处理接受类型给定的options的结果。
-
让starting directory成为 确定选择器将起始的目录 给定的options["
id"], options["startIn"], 和environment的结果。 -
让global成为environment的全局对象。
-
验证environment 是否允许显示文件选择器。
-
让p成为一个新承诺。
-
在并行运行以下步骤:
-
可选地,等待任何先前执行此算法的终止。
-
让filePickerOptions成为一个空的映射。
-
设置filePickerOptions["multiple"]为options["
multiple"]。 -
让dismissed成为WebDriver BiDi文件对话框打开的null和filePickerOptions的结果。
-
如果dismissed为false:
-
向用户显示一个提示,要求用户选择一些文件。 如果filePickerOptions["multiple"]为false,则最多只能选择一个文件; 否则可以选择任意数量。
显示的提示应该让用户从accepts options中选择一个来过滤显示的文件列表。 确切如何实现,以及这个提示看起来如何是实现定义的。
当可能时,此提示应该从starting directory开始显示。
-
等待用户做出选择。
-
-
如果dismissed为true或用户在不做选择的情况下解散提示, 拒绝 p为一个"
AbortError"DOMException并中止。 -
让result成为一个空的列表。
-
对于 entries中的每个entry:
-
如果entry被用户代理视为对这个网站太敏感或危险:
-
通知用户所选的文件或目录不能暴露给这个网站。
-
在用户代理的自由裁量下, 要么回到这些并行步骤的开始, 要么拒绝 p为一个"
AbortError"DOMException并中止。
-
-
将一个与entry关联的新
FileSystemFileHandle添加到result。
-
-
注意: 这让网站 立即可以在返回的句柄上执行可能需要用户激活的操作,例如请求更多权限。
-
解决 p为result。
-
-
返回p。
- handle = await window .
showSaveFilePicker( options ) -
显示一个文件选择器,让用户选择单个文件,返回所选文件的句柄。所选文件不必已经存在。如果所选文件不存在,则在此方法返回之前创建一个新的空文件,否则在此方法返回之前清除现有文件。
- handle = await window .
showSaveFilePicker({suggestedName: "README.md" }) -
显示一个文件选择器,默认文件名预填为"README.md"作为要保存的文件名。
可以向
showSaveFilePicker()传递额外options来指示网站想要用户选择的文件的类型和文件选择器将打开的目录。请参见§ 3.2 文件选择器选项了解详情。 -
让accepts options成为处理接受类型给定的options的结果。
-
让starting directory成为 确定选择器将起始的目录 给定的options["
id"], options["startIn"] 和environment的结果。 -
让global成为environment的全局对象。
-
验证environment 是否允许显示文件选择器。
-
让p成为一个新承诺。
-
在并行运行以下步骤:
-
可选地,等待任何先前执行此算法的终止。
-
向用户显示一个提示,要求用户选择一个文件。 显示的提示应该让用户从accepts options中选择一个来过滤显示的文件列表。 确切如何实现,以及这个提示看起来如何是实现定义的。 如果accepts options在UI中显示,所选选项也应该用于建议一个扩展 来附加到用户提供的文件名,但这不是必需的。特别是用户代理可以自由忽略 潜在危险的后缀,例如以
".lnk"或".local"结尾的那些。当可能时,此提示应该从starting directory开始显示。
如果options["
suggestedName"] 存在并且 不是null,文件选择器提示将预填为 options["suggestedName"] 作为默认名称 来保存。suggestedName和accepts options之间的交互是实现定义的。 如果suggestedName被视为太危险,用户代理应该忽略或清理建议的文件名,类似于获取某些东西时的清理作为下载。注意: 用户代理可以例如选择accepts options中与
suggestedName匹配的任何选项作为默认过滤器。 -
等待用户做出选择。
-
如果用户在不做选择的情况下解散提示, 拒绝 p为一个"
AbortError"DOMException并中止。 -
让entry成为一个文件条目 表示所选文件。
-
如果entry被用户代理视为对这个网站太敏感或危险:
-
通知用户所选的文件或目录不能暴露给这个网站。
-
在用户代理的自由裁量下, 要么回到这些并行步骤的开始, 要么拒绝 p为一个"
AbortError"DOMException并中止。
-
-
将result设置为一个与entry关联的新
FileSystemFileHandle。 -
在global的激活通知步骤中执行 global的浏览上下文。
注意: 这让网站 立即可以在返回的句柄上执行可能需要用户激活的操作,例如请求更多权限。
-
解决 p为result。
-
-
返回p。
- handle = await window .
showDirectoryPicker()- handle = await window .
showDirectoryPicker()({mode: 'read' }) - handle = await window .
-
显示一个目录选择器,让用户选择单个目录,如果用户授予读取权限,则返回所选目录的句柄。
- handle = await window .
showDirectoryPicker()({mode: 'readwrite' }) -
显示一个目录选择器,让用户选择单个目录,返回所选目录的句柄。用户代理可以将对此句柄的读取和写入权限请求组合到一个后续提示中。
-
让starting directory成为确定选择器将起始的目录 给定的options["
id"], options["startIn"] 和environment的结果。 -
让global成为environment的全局对象。
-
验证environment 是否允许显示文件选择器。
-
让p成为一个新承诺。
-
在并行运行以下步骤:
-
可选地,等待任何先前执行此算法的终止。
-
让filePickerOptions成为一个空的映射。
-
将filePickerOptions["multiple"]设置为false。
-
让dismissed成为WebDriver BiDi文件对话框打开的null和filePickerOptions的结果。
-
如果dismissed为false:
-
向用户显示一个提示,要求用户选择一个目录。
当可能时,此提示应该从starting directory开始显示。
-
等待用户做出选择。
-
-
如果dismissed为true或用户在不做选择的情况下解散提示, 拒绝 p为一个"
AbortError"DOMException并中止。 -
让entry成为一个目录条目表示所选目录。
-
如果entry被用户代理视为对这个网站太敏感或危险:
-
通知用户所选的文件或目录不能暴露给这个网站。
-
在用户代理的自由裁量下, 要么回到这些并行步骤的开始, 要么拒绝 p为一个"
AbortError"DOMException并中止。
-
-
将result设置为一个与entry关联的新
FileSystemDirectoryHandle。 -
让desc成为一个
FileSystemPermissionDescriptor。 -
将desc["
name"] 设置为 "file-system"。 -
将desc["
handle"] 设置为result。 -
让status成为运行创建一个PermissionStatus给定的desc的结果。
-
请求使用 desc的权限。
-
在desc和status上运行默认权限查询算法。
-
如果status不是"
granted", 拒绝 result为一个"AbortError"DOMException并中止。 -
解决 p为result。
-
-
返回p。
- handle = await item .
getAsFileSystemHandle() -
如果拖拽的项目是文件,则返回
FileSystemFileHandle对象;如果拖拽的项目是目录,则返回FileSystemDirectoryHandle对象。 -
如果
DataTransferItem对象不在读取/写入模式或只读模式,则返回以null解析的承诺。 -
如果拖拽数据项种类不是File,则返回以null解析的承诺。
-
让p成为一个新承诺。
-
在并行运行以下步骤:
-
让entry成为代表拖拽文件或目录的文件系统条目。
-
如果entry是一个文件条目:
-
让handle成为与entry关联的
FileSystemFileHandle。
-
-
否则如果entry是一个目录条目:
-
让handle成为与entry关联的
FileSystemDirectoryHandle。
-
-
解决 p为entry。
-
-
返回p。
-
包含用户代理本身的目录或目录。
-
用户代理存储网站存储的目录。
-
包含系统文件的目录(例如Windows上的
C:\Windows)。 -
在Linux上如
/dev/、/sys和/proc这样的目录,会给予对低级设备的访问权限。 -
用户的整个"home"目录。home目录内的单个文件和目录应该仍然允许,但用户代理通常不应让用户给予对整个目录的空白访问权限。
-
下载的默认目录,如果用户代理有这样的东西。目录内的单个文件再次应该允许,但整个目录会冒着泄露用户意识到的更多数据的风险。
-
名称以
.lnk结尾的文件,当选择要写入的文件时。在Windows上写入这些文件类似于在其他操作系统上创建符号链接,因此可以用于尝试欺骗用户给予对他们未打算暴露的文件的访问权限。 -
名称以
.local结尾的文件,当选择要写入的文件时。Windows使用这些文件来决定加载哪些DLL,因此写入这些文件可以用于导致代码被执行。
StartInDirectory
startIn和一个环境设置对象 environment,
运行以下步骤:
3.3.
showOpenFilePicker()
方法
showOpenFilePicker(options)方法,
当被调用时,必须运行
以下步骤:
3.4.
showSaveFilePicker()
方法
suggestedName
选项在Chrome 91中首次引入。
showSaveFilePicker(options)方法,
当被调用时,必须运行
以下步骤:
3.5.
showDirectoryPicker()
方法
id
和startIn
字段的行为
与id
和startIn
字段相同,分别。
请参见§ 3.2.2 起始目录了解如何使用这些字段的详情。
showDirectoryPicker(options)
方法,当被调用时,必须运行
以下步骤:
3.6. 拖拽
partial interface DataTransferItem {Promise <FileSystemHandle ?>getAsFileSystemHandle (); };
在拖拽操作期间,拖拽的文件和目录项分别与文件条目和目录条目关联。
getAsFileSystemHandle()方法的步骤是:
elem. addEventListener( 'dragover' , ( e) => { // Prevent navigation. e. preventDefault(); }); elem. addEventListener( 'drop' , async ( e) => { e. preventDefault(); const fileHandlesPromises= [... e. dataTransfer. items] . filter( item=> item. kind=== 'file' ) . map( item=> item. getAsFileSystemHandle()); for await ( const handleof fileHandlesPromises) { if ( handle. kind=== 'directory' ) { console. log( `Directory: ${ handle. name} ` ); } else { console. log( `File: ${ handle. name} ` ); } } });
这目前不会阻止对太敏感或危险目录的访问,以与其他给出对已删除文件和目录访问权限的API保持一致。这与本地文件系统句柄工厂不一致,所以我们可能会重新考虑这个问题。
4. 无障碍性考虑
此部分是非规范性的。
当此规范用于在用户界面中呈现信息时,实现者将希望遵循平台级无障碍性指南。
5. 隐私考虑
此部分是非规范性的。
此API不会给网站提供比现有的<input type=file>和<input type=file webkitdirectory>API更多的读取数据访问权限。此外,与那些API类似,所有对文件和目录的访问都明确地通过文件或目录选择器进行门控。
然而,此新API有几个主要的隐私风险:
5.1. 用户无意中给予网站对更多或更敏感文件的访问权限。
这不是此API的新风险,但用户代理应该尝试确保用户知道他们确切地在给网站什么访问权限。当给予对目录的访问权限时,这一点特别重要,因为用户可能不会立即清楚该目录中实际上有多少文件。
一个相关风险是用户给予对特别敏感数据的访问权限。这可能包括用户代理的一些配置数据、网络缓存或cookie存储,或操作系统配置数据如密码文件。为了防范这一点,用户代理被鼓励限制用户可以在目录选择器中选择哪些目录,甚至可能限制用户可以选择哪些文件。这将使意外给予对包含特别敏感数据的目录的访问权限变得更加困难。必须小心在限制API可以访问的内容与保持API有用性之间取得正确平衡。毕竟,此API有意让用户使用网站与他们一些最私密的个人数据进行交互。
用户代理可能想要限制为太敏感或危险的目录示例包括:
5.2. 网站尝试使用此API进行跟踪。
此API可被网站用于跨清除浏览数据跟踪用户。这是因为,与现有的文件访问API相反,用户代理能够授予对文件或目录的持久访问权限,并可以重新提示。在结合能够写入文件的权限的情况下,网站将能够在用户的磁盘上持久化一个标识符。清除浏览数据不会以任何方式影响那些文件,使这些标识符在这些操作中持久存在。
此风险在一定程度上通过清除浏览数据将清除网站已持久化的所有句柄(例如在IndexedDB中)这一事实得到缓解,因此在浏览数据被清除后,网站不会有任何句柄来重新请求权限。此外,用户代理被鼓励使网站有权访问的文件和目录清晰,并自动使权限授予过期,除非对于特别受信任的起源(例如持久权限可以限于"安装的"web应用程序)。
用户代理还被鼓励提供一种让用户撤销授予权限的方式。清除浏览数据预计也会撤销所有权限。
5.3. 第一方与第三方上下文。
在第三方上下文(例如起源与顶级框架不匹配的iframe)中,网站无法获得他们还没有访问权限的数据。这包括通过本地文件系统句柄工厂获取对新文件或目录的访问权限,以及通过requestPermissionAPI请求对现有句柄的更多权限。
句柄也可以只发布消息到同源目的地。尝试将句柄发送到跨源目的地将导致messageerror事件。
6. 安全考虑
此部分是非规范性的。
此API给予网站修改磁盘上现有文件的能力,以及写入新文件的能力。这有几个重要的安全考虑:
6.1. 恶意软件
此API可被网站用于尝试在用户系统上存储和/或执行恶意软件。为了缓解此风险,此API不提供任何方式标记文件为可执行(另一方面,已经可执行的文件很可能保持那样,即使文件通过此API被修改)。此外,用户代理被鼓励对通过此API创建或修改的文件应用如Mark-of-the-Web之类的措施。
最后,用户代理被鼓励通过恶意软件扫描和安全浏览检查验证通过此API修改的文件的内容,除非已经存在某种外部强大信任关系。当然,这对此API的性能特征有影响。
6.2. 勒索软件攻击
另一个风险因素是勒索软件攻击。上文描述的关于阻止对某些敏感目录的访问的限制有助于限制此类攻击可以造成的损害。此外,用户代理可以在他们认为合适的粒度上授予对文件的写入访问权限。
6.3. 填满用户的磁盘
除了桶文件系统中的文件,通过此API写入的文件不受存储配额限制。因此,网站可以填满用户的磁盘而不会被配额限制,这可能使用户的设备处于不良状态(请注意,即使是受存储配额限制的存储,也仍然可能填满或接近填满用户的磁盘,因为存储配额通常不依赖于可用磁盘空间的数量)。
没有此API,网站可以通过触发大文件的下载(潜在地客户端创建,以不产生任何网络开销)来写入不受配额限制的磁盘数据。虽然truncate()的存在和在文件末尾之外的潜在真正大偏移处写入使得创建大文件变得更容易和成本更低,但在大多数文件系统上,这样的文件不应实际占用那么多磁盘空间,因为最常用的文件系统支持稀疏文件(因此不会实际存储通过调整文件大小或寻求超过文件末尾而生成的NUL字节)。
无论用户代理使用什么缓解措施来防范网站通过配额管理的存储或现有下载机制填满磁盘,当网站使用此API写入磁盘时,也应该采用相同的措施。