1. 引言
本节是资料性的。
Web 应用应具备处理尽可能广泛的用户输入的能力,包括用户希望上传到远程服务器或在丰富的 Web 应用中操作的文件。本规范定义了文件的基本表示形式、文件列表、因访问文件而引发的错误,以及读取文件的编程方式。此外,本规范还定义了一个接口,用于表示可以在符合要求的用户代理的主线程上异步处理的“原始数据”。本规范中定义的接口和 API 可以与暴露给 Web 平台的其他接口和 API 一起使用。
File 接口表示通常从底层文件系统获取的文件数据,而 Blob 接口(“二进制大对象”——一个最初在 Google Gears 中引入到 Web API 的名称)表示不可变的原始数据。File 或 Blob 读取应在主线程上异步进行,并在线程化的 Web 应用中使用可选的同步 API。用于读取文件的异步 API 可以防止阻塞用户代理的主线程并导致 UI“冻结”。本规范定义了一个基于 *事件模型* 的异步 API,用于读取和访问 File 或 Blob 的数据。FileReader 对象提供异步读取方法,通过事件处理器内容属性和触发事件来访问该文件的数据。事件和事件处理器的使用允许独立的代码块监控 *读取进度*(这对远程驱动器或挂载驱动器特别有用,因为这些驱动器的文件访问性能可能与本地驱动器不同)以及在读取文件过程中可能出现的错误状况。一个示例将有助于说明。
function startRead() { // obtain input element through DOM var file= document. getElementById( 'file' ). files[ 0 ]; if ( file){ getAsText( file); } } function getAsText( readFile) { var reader= new FileReader(); // Read file into memory as UTF-16 reader. readAsText( readFile, "UTF-16" ); // Handle progress, success, and errors reader. onprogress= updateProgress; reader. onload= loaded; reader. onerror= errorHandler; } function updateProgress( evt) { if ( evt. lengthComputable) { // evt.loaded and evt.total are ProgressEvent properties var loaded= ( evt. loaded/ evt. total); if ( loaded< 1 ) { // Increase the prog bar length // style.width = (loaded * 200) + "px"; } } } function loaded( evt) { // Obtain the read file data var fileString= evt. target. result; // Handle UTF-16 file dump if ( utils. regexp. isChinese( fileString)) { //Chinese Characters + Name validation } else { // run other charset test } // xhr.send(fileString) } function errorHandler( evt) { if ( evt. target. error. name== "NotReadableError" ) { // The file could not be read } }
2. 术语与算法
当本规范提到终止算法时,用户代理必须在完成当前步骤后终止该算法。本规范中定义的异步 读取方法 可能会在该算法终止之前返回,并可以通过 abort() 调用来终止。
本规范中的算法和步骤使用以下数学运算
-
max(a,b) 返回 a 和 b 中的最大值,并且始终对整数执行,正如 WebIDL [WebIDL] 中定义的那样;例如 max(6,4) 的结果为 6。此操作也定义在 ECMAScript [ECMA-262] 中。
-
min(a,b) 返回 a 和 b 中的最小值,并且始终对整数执行,正如 WebIDL [WebIDL] 中定义的那样;例如 min(6,4) 的结果为 4。此操作也定义在 ECMAScript [ECMA-262] 中。
-
数学比较(如 <(小于)、≤(小于或等于)和 >(大于))与 ECMAScript [ECMA-262] 中的定义相同。
术语 Unix 纪元(Unix Epoch) 在本规范中用于指代 1970 年 1 月 1 日 00:00:00 UTC(或 1970-01-01T00:00:00Z ISO 8601)的时间;这与 ECMA-262 [ECMA-262] 中概念上的“0”时间相同。
Blob blob、start、end 和 contentType 时,用于指代以下步骤,并返回一个新的 Blob,其中包含从 start 参数范围到但不包括 end 参数的字节。它必须按如下方式执行-
令 originalSize 为 blob 的
size。 -
start 参数(如果非空)是 切片 blob 调用的起始点值,并且必须被视为字节顺序位置,第 0 位表示第一个字节。用户代理必须根据以下规则标准化 start
- 如果 start 为空,令 relativeStart 为 0。
- 如果 start 为负数,令 relativeStart 为
max((originalSize + start), 0)。 - 否则,令 relativeStart 为
min(start, originalSize)。
-
end 参数(如果非空)是 切片 blob 调用的结束点值。用户代理必须根据以下规则标准化 end
- 如果 end 为空,令 relativeEnd 为 originalSize。
- 如果 end 为负数,令 relativeEnd 为
max((originalSize + end), 0)。 - 否则,令 relativeEnd 为
min(end, originalSize)。
-
contentType 参数(如果非空)用于设置以 ASCII 编码的小写字符串,表示
Blob的媒体类型。用户代理必须根据以下规则标准化 contentType- 如果 contentType 为空,令 relativeContentType 设置为空字符串。
- 否则,令 relativeContentType 设置为 contentType 并运行以下子步骤
-
如果 relativeContentType 包含 U+0020 到 U+007E 范围之外的任何字符,则将 relativeContentType 设置为空字符串并从这些子步骤返回。
-
将 relativeContentType 中的每个字符转换为 ASCII 小写。
-
-
令 span 为
max((relativeEnd - relativeStart), 0)。 -
返回一个具有以下特征的新
Blob对象 S
3. Blob 接口与二进制数据
Blob 对象指代一个 字节 序列,并具有一个 size 属性(它是字节序列中的总字节数)和一个 type 属性(它是以 ASCII 编码的小写字符串,表示 字节 序列的媒体类型)。
每个 Blob 必须具有一个内部的 快照状态(snapshot state),如果存在任何此类底层存储,它必须初始设置为底层存储的状态。关于 快照状态 的进一步规范定义可以在 File 中找到。
[Exposed =(Window ,Worker ),Serializable ]interface {Blob (constructor optional sequence <BlobPart >blobParts ,optional BlobPropertyBag = {});options readonly attribute unsigned long long size ;readonly attribute DOMString type ; // slice Blob into byte-ranged chunksBlob slice (optional [Clamp ]long long ,start optional [Clamp ]long long ,end optional DOMString ); // read from the Blob. [contentType NewObject ]ReadableStream stream (); [NewObject ]Promise <USVString >text (); [NewObject ]Promise <ArrayBuffer >arrayBuffer (); [NewObject ]Promise <Uint8Array >bytes (); };enum {EndingType ,"transparent" };"native" dictionary {BlobPropertyBag DOMString type = "";EndingType endings = "transparent"; };typedef (BufferSource or Blob or USVString );BlobPart
Blob 对象是 可序列化对象。给定 value 和 serialized,其 序列化步骤 为:
-
将 serialized.[[SnapshotState]] 设置为 value 的 快照状态。
-
将 serialized.[[ByteSequence]] 设置为 value 的底层字节序列。
给定 serialized 和 value,其 反序列化步骤 为:
-
将 value 的 快照状态 设置为 serialized.[[SnapshotState]]。
-
将 value 的底层字节序列设置为 serialized.[[ByteSequence]]。
Blob blob 具有一个关联的 获取流(get stream) 算法,该算法执行以下步骤:-
令 stream 为在 blob 的 相关 Realm 中创建的 新
ReadableStream。 -
设置 具有字节读取支持的 stream。
-
并行运行以下步骤
-
当并非 blob 的所有字节都已读取时:
-
令 bytes 为从 blob 中读取 数据块(chunk) 时产生的 字节序列;如果无法读取数据块,则为失败。
-
在全局任务队列中排队 文件读取任务源 的任务,给定 blob 的 相关全局对象,以执行以下步骤:
-
令 chunk 为包装了包含 bytes 的
ArrayBuffer的新Uint8Array。如果创建ArrayBuffer抛出异常,则以该异常 报错 stream 并中止这些步骤。 -
将 chunk 加入队列(Enqueue) 到 stream 中。
-
-
-
返回 stream。
3.1. 构造函数
Blob() 构造函数可以使用零个或多个参数调用。当调用 Blob() 构造函数时,用户代理必须运行以下步骤:
3.1.1. 构造函数参数
Blob() 构造函数可以使用以下参数调用:
- 一个
blobParts序列(sequence) - 它接受任意数量的以下类型的元素,并且顺序不限:
-
BufferSource元素。 -
Blob元素。 -
USVString元素。
-
- 一个 可选的
BlobPropertyBag - 它接受这些可选成员:
-
type,表示Blob媒体类型的以 ASCII 编码的小写字符串。该成员的规范条件在 § 3.1 构造函数 中提供。 -
endings,一个枚举,可取值"transparent"或"native"。默认情况下设置为"transparent"。如果设置为"native",blobParts中的任何USVString元素中的 换行符将被转换为原生格式。
-
BlobPart 的 parts 和 BlobPropertyBag options,运行以下步骤:-
令 bytes 为空字节序列。
-
对于 parts 中的每个 element:
-
如果 element 是
USVString,运行以下子步骤: -
如果 element 是
BufferSource,则 获取缓冲区源所持有的字节副本,并将这些字节追加到 bytes。 -
如果 element 是
Blob,则将它表示的字节追加到 bytes。
-
-
返回 bytes。
-
令 native line ending 为 码点 U+000A LF。
-
如果底层平台的约定是将换行符表示为回车和换行序列,则将 native line ending 设置为 码点 U+000D CR 后跟 码点 U+000A LF。
-
将 result 设置为空 字符串。
-
令 position 为 s 的 位置变量,最初指向 s 的起始处。
-
令 token 为 收集 从 s 中给定的 position 开始且不等于 U+000A LF 或 U+000D CR 的 码点序列 的结果。
-
将 token 追加到 result。
-
当 position 未超过 s 的末尾时:
-
如果 s 中 position 处的 码点 等于 U+000D CR:
-
将 native line ending 追加到 result。
-
将 position 前进 1。
-
如果 position 未超过 s 的末尾且 s 中 position 处的 码点 等于 U+000A LF,则将 position 前进 1。
-
-
否则,如果 s 中 position 处的 码点 等于 U+000A LF,则将 position 前进 1 并将 native line ending 追加到 result。
-
令 token 为 收集 从 s 中给定的 position 开始且不等于 U+000A LF 或 U+000D CR 的 码点序列 的结果。
-
将 token 追加到 result。
-
-
返回 result。
// Create a new Blob object var a= new Blob(); // Create a 1024-byte ArrayBuffer // buffer could also come from reading a File var buffer= new ArrayBuffer( 1024 ); // Create ArrayBufferView objects based on buffer var shorts= new Uint16Array( buffer, 512 , 128 ); var bytes= new Uint8Array( buffer, shorts. byteOffset+ shorts. byteLength); var b= new Blob([ "foobarbazetcetc" + "birdiebirdieboo" ], { type: "text/plain;charset=utf-8" }); var c= new Blob([ b, shorts]); var a= new Blob([ b, c, bytes]); var d= new Blob([ buffer, b, c, bytes]);
3.2. 属性
size, 类型为 unsigned long long,只读- 返回字节序列的大小(以字节为单位)。在获取时,符合要求的用户代理必须返回可由
FileReader或FileReaderSync对象读取的总字节数;如果Blob没有可读取的字节,则返回 0。 type, 类型为 DOMString,只读- 以 ASCII 编码的小写字符串,表示
Blob的媒体类型。在获取时,用户代理必须将Blob的类型作为 ASCII 编码的小写字符串返回,使得当其转换为 字节 序列时,它是一个 可解析的 MIME 类型;或者,如果类型无法确定,则返回空字符串(0 字节)。type属性可以由 Web 应用本身通过构造函数调用和slice()调用设置;在这些情况下,该属性的进一步规范条件分别在 § 3.1 构造函数、§ 4.1 构造函数 和 § 3.3.1 slice() 方法 中。用户代理也可以确定Blob的type,特别是如果 字节 序列来自磁盘文件;在这种情况下,进一步的规范条件在 文件类型指南 中。注意:如果对从表示 Blob 对象类型的 ASCII 编码字符串转换而来的字节序列执行 解析 MIME 类型 算法不返回失败,则
Blob的类型 t 被视为 可解析的 MIME 类型。注意:
type属性的使用会通知 包装数据(package data) 算法,并在 获取(fetching) blob URL 时确定Content-Type标头。
3.3. 方法与参数
3.3.1. slice() 方法
slice() 方法返回一个新的 Blob 对象,其中包含从可选的 start 参数范围到但不包括可选的 end 参数的字节,并且具有一个 type 属性,该属性为可选的 contentType 参数的值。它必须按如下方式执行:-
令 sliceStart、sliceEnd 和 sliceContentType 为 null。
-
如果给出了 start,将 sliceStart 设置为 start。
-
如果给出了 end,将 sliceEnd 设置为 end。
-
如果给出了 contentType,将 sliceContentType 设置为 contentType。
-
返回给定 this、sliceStart、sliceEnd 和 sliceContentType 的 切片 blob 的结果。
slice() 调用类型。由于 File 接口继承自 Blob 接口,因此示例基于 File 接口的使用。// obtain input element through DOM var file= document. getElementById( 'file' ). files[ 0 ]; if ( file) { // create an identical copy of file // the two calls below are equivalent var fileClone= file. slice(); var fileClone2= file. slice( 0 , file. size); // slice file into 1/2 chunk starting at middle of file // Note the use of negative number var fileChunkFromEnd= file. slice( - ( Math. round( file. size/ 2 ))); // slice file into 1/2 chunk starting at beginning of file var fileChunkFromStart= file. slice( 0 , Math. round( file. size/ 2 )); // slice file from beginning till 150 bytes before end var fileNoMetadata= file. slice( 0 , - 150 , "application/experimental" ); }
3.3.2. stream() 方法
stream() 方法在调用时,必须返回对 this 调用 获取流(get stream) 的结果。
3.3.3. text() 方法
text() 方法在调用时,必须运行这些步骤:
-
令 reader 为从 stream 获取读取器(getting a reader) 的结果。如果这抛出了异常,则返回一个被该异常拒绝的新 Promise。
-
令 promise 为使用 reader 从 stream 读取所有字节(reading all bytes) 的结果。
-
返回通过一个 fulfillment 处理程序转换 promise 的结果,该处理程序返回对其第一个参数运行 UTF-8 解码 的结果。
注意:这与 readAsText() 的行为不同,以便更好地与 Fetch 的 text() 行为保持一致。具体而言,此方法将始终使用 UTF-8 作为编码,而 FileReader 可以根据 blob 的类型和传入的编码名称使用不同的编码。
3.3.4. arrayBuffer() 方法
arrayBuffer() 方法在调用时,必须运行这些步骤:
-
令 reader 为从 stream 获取读取器 的结果。如果这抛出了异常,则返回一个被该异常拒绝的新 Promise。
-
令 promise 为使用 reader 从 stream 读取所有字节 的结果。
-
返回通过一个 fulfillment 处理程序转换 promise 的结果,该处理程序返回一个内容为该 fulfillment 处理程序第一个参数的新
ArrayBuffer。
3.3.5. bytes() 方法
bytes() 方法在调用时,必须运行这些步骤:
-
令 reader 为从 stream 获取读取器 的结果。如果这抛出了异常,则返回一个被该异常拒绝的新 Promise。
-
令 promise 为使用 reader 从 stream 读取所有字节 的结果。
-
返回通过一个 fulfillment 处理程序转换 promise 的结果,该处理程序返回一个包装了包含其第一个参数的
ArrayBuffer的新Uint8Array。
4. File 接口
File 对象是一个带有 name 属性的 Blob 对象(该属性为字符串);它可以通过构造函数在 Web 应用中创建,或者是指代来自底层(OS)文件系统的文件 字节 序列的引用。
如果 File 对象是指代源自磁盘文件的 字节 序列的引用,则其 快照状态 应设置为 File 对象创建时磁盘上文件的状态。
注意:对于用户代理而言,这是一项非平凡的实现需求,因此这不是一项 *必须(must)* 而是 *应该(should)* [RFC2119] 的要求。用户代理应努力使 File 对象的 快照状态 在获取引用时设置为底层存储在磁盘上的状态。如果在获取引用后文件在磁盘上被修改,则 File 的 快照状态 将与底层存储的状态不同。用户代理可以使用修改时间戳和其他机制来维持 快照状态,但这留作实现细节。
当 File 对象指代磁盘上的文件时,用户代理必须返回该文件的 type,并且必须遵循下方的 文件类型指南(file type guidelines):
-
用户代理必须将
type作为以 ASCII 编码的小写字符串返回,使得当其转换为相应的字节序列时,它是一个 可解析的 MIME 类型;或者,如果类型无法确定,则为空字符串(0 字节)。 -
当文件类型为
text/plain时,用户代理不得在媒体类型的 参数字典(dictionary of parameters) 部分追加 charset 参数 [MIMESNIFF]。 -
用户代理不得尝试启发式确定编码,包括统计方法。
[Exposed =(Window ,Worker ),Serializable ]interface :File Blob {(constructor sequence <BlobPart >fileBits ,USVString fileName ,optional FilePropertyBag = {});options readonly attribute DOMString name ;readonly attribute long long lastModified ; };dictionary :FilePropertyBag BlobPropertyBag {long long lastModified ; };
File 对象是 可序列化对象。给定 value 和 serialized,其 序列化步骤 为:
-
将 serialized.[[SnapshotState]] 设置为 value 的 快照状态。
-
将 serialized.[[ByteSequence]] 设置为 value 的底层字节序列。
-
将 serialized.[[Name]] 设置为 value 的
name属性的值。 -
将 serialized.[[LastModified]] 设置为 value 的
lastModified属性的值。
给定 value 和 serialized,其 反序列化步骤 为:
-
将 value 的 快照状态 设置为 serialized.[[SnapshotState]]。
-
将 value 的底层字节序列设置为 serialized.[[ByteSequence]]。
-
将 value 的
name属性的值初始化为 serialized.[[Name]]。 -
将 value 的
lastModified属性的值初始化为 serialized.[[LastModified]]。
4.1. 构造函数
File 构造函数使用两个或三个参数调用,具体取决于是否使用了可选的字典参数。当调用 File() 构造函数时,用户代理必须运行以下步骤:-
令 bytes 为给定
fileBits和options处理 blob 部分 的结果。 -
令 n 为传递给构造函数的
fileName参数。注意:底层操作系统文件系统对文件名使用不同的约定;对于构造的文件,强制使用 UTF-16 可减少文件名转换为 字节 序列时的歧义。
-
通过运行以下子步骤处理
FilePropertyBag字典参数:-
如果提供了
type成员且不是空字符串,则令 t 设置为type字典成员。如果 t 包含 U+0020 到 U+007E 范围之外的任何字符,则将 t 设置为空字符串并从这些子步骤返回。 -
将 t 中的每个字符转换为 ASCII 小写。
-
如果提供了
lastModified成员,令 d 设置为lastModified字典成员。如果未提供,将 d 设置为当前的日期和时间,表示为自 Unix 纪元 以来的毫秒数(这等同于Date.now()[ECMA-262])。注意:由于 ECMA-262
Date对象转换为表示自 Unix 纪元 以来毫秒数的long long值,因此lastModified成员可以是Date对象 [ECMA-262]。
-
-
返回一个新的
File对象 F,使得:-
F 指代 bytes 字节 序列。
-
F.
size设置为 bytes 中的总字节数。 -
F.
name设置为 n。 -
F.
type设置为 t。 -
F.
lastModified设置为 d。
-
4.1.1. 构造函数参数
File() 构造函数可以使用以下参数调用:
- 一个
fileBits序列(sequence) - 它接受任意数量的以下元素,并且顺序不限:
-
BufferSource元素。 -
USVString元素。
-
- 一个
fileName参数 - 一个表示文件名的
USVString参数;此构造函数参数的规范条件可在 § 4.1 构造函数 中找到。 - 一个可选的
FilePropertyBag字典 - 它除了
BlobPropertyBag的 成员 外,还接受一个成员:-
一个可选的
lastModified成员,它必须是long long类型;此成员的规范条件在 § 4.1 构造函数 中提供。
-
4.2. 属性
name, 类型为 DOMString,只读- 文件名。在获取时,这必须返回作为字符串的文件名。不同的底层操作系统文件系统使用了众多的文件名变体和约定;这仅仅是文件名,不包含路径信息。在获取时,如果用户代理无法使此信息可用,则必须返回空字符串。如果
File对象是使用构造函数创建的,则该属性的进一步规范条件可在 § 4.1 构造函数 中找到。 lastModified, 类型为 long long,只读- 文件的最后修改日期。在获取时,如果用户代理可以使此信息可用,则必须返回一个
long long,设置为文件最后一次修改的时间,即自 Unix 纪元 以来的毫秒数。如果最后修改日期和时间未知,则该属性必须返回当前日期和时间作为代表自 Unix 纪元 以来的毫秒数的long long值;这等同于Date[ECMA-262]。如果. now() File对象是使用构造函数创建的,则该属性的进一步规范条件可在 § 4.1 构造函数 中找到。
File 接口在暴露类型为 FileList 属性的对象上可用;这些对象在 HTML [HTML] 中定义。File 接口继承自 Blob,它是不可变的,因此表示在启动 读取操作 时可以读入内存的文件数据。用户代理必须将那些在读取时已不存在的文件的读取处理为 错误,如果在 Web Worker [Workers] 上使用 FileReaderSync,则抛出 NotFoundError 异常;或者触发一个带有 error 属性(返回 NotFoundError)的 错误 事件。
var file= document. getElementById( "filePicker" ). files[ 0 ]; var date= new Date( file. lastModified); println( "You selected the file " + file. name+ " which was modified on " + date. toDateString() + "." ); ... // Generate a file with a specific last modified date var d= new Date( 2013 , 12 , 5 , 16 , 23 , 45 , 600 ); var generatedFile= new File([ "Rough Draft ...." ], "Draft1.txt" , { type: "text/plain" , lastModified: d}) ...
5. The FileList Interface
注意:FileList 接口应被视为“有风险”,因为 Web 平台的总体趋势是将此类接口替换为 ECMAScript [ECMA-262] 中的 Array 平台对象。特别是,这意味着 filelist 这种语法是有风险的;FileList 的大多数其他编程使用不太可能受到最终迁移到 Array 类型的影响。
此接口是 File 对象的列表。
[Exposed =(Window ,Worker ),Serializable ]interface {FileList getter File ?item (unsigned long index );readonly attribute unsigned long length ; };
FileList 对象是 可序列化对象。给定 value 和 serialized,其 序列化步骤 为:
-
将 serialized.[[Files]] 设置为一个空 列表。
-
对于 value 中的每个 file,将 file 的 子序列化(sub-serialization) 追加到 serialized.[[Files]] 中。
给定 serialized 和 value,其 反序列化步骤 为:
-
对于 serialized.[[Files]] 中的每个 file,将 file 的 子反序列化(sub-deserialization) 添加到 value 中。
<input type="file"> 元素的 DOM 访问,然后访问所选文件。// uploadData is a form element // fileChooser is input element of type 'file' var file= document. forms[ 'uploadData' ][ 'fileChooser' ]. files[ 0 ]; // alternative syntax can be // var file = document.forms['uploadData']['fileChooser'].files.item(0); if ( file) { // Perform file ops }
5.1. 属性
length, 类型为 unsigned long,只读- 必须返回
FileList对象中的文件数量。如果没有文件,该属性必须返回 0。
5.2. 方法与参数
item(index)- 必须返回
FileList中的第 index 个File对象。如果FileList中不存在第 index 个File对象,则该方法必须返回null。index必须由用户代理视为FileList中File对象位置的值,0 表示第一个文件。支持的属性索引(Supported property indices) 是范围从 0 到由FileList对象表示的File对象数量减 1 的数字。如果没有此类File对象,则没有支持的属性索引。
注意:HTMLInputElement 接口具有一个类型为 FileList 的只读属性,这就是上述示例中正在访问的内容。其他具有类型为 FileList 的只读属性的接口包括 DataTransfer 接口。
6. 读取数据
6.1. 文件读取任务源
本规范定义了一个新的通用 任务源(task source),称为 文件读取任务源(file reading task source),它用于本规范中所有 排队任务,以读取与 Blob 和 File 对象关联的字节序列。它应被用于响应异步读取二进制数据而触发的功能。
6.2. FileReader API
[Exposed =(Window ,Worker )]interface :FileReader EventTarget {constructor (); // async read methodsundefined readAsArrayBuffer (Blob );blob undefined readAsBinaryString (Blob );blob undefined readAsText (Blob ,blob optional DOMString );encoding undefined readAsDataURL (Blob );blob undefined abort (); // statesconst unsigned short = 0;EMPTY const unsigned short = 1;LOADING const unsigned short = 2;DONE readonly attribute unsigned short readyState ; // File or Blob datareadonly attribute (DOMString or ArrayBuffer )?result ;readonly attribute DOMException ?error ; // event handler content attributesattribute EventHandler onloadstart ;attribute EventHandler onprogress ;attribute EventHandler onload ;attribute EventHandler onabort ;attribute EventHandler onerror ;attribute EventHandler onloadend ; };
FileReader 具有一个关联的 状态(state),即 "empty"、"loading" 或 "done"。它最初为 "empty"。
FileReader 具有一个关联的 结果(result)(null、DOMString 或 ArrayBuffer)。它最初为 null。
FileReader 具有一个关联的 错误(error)(null 或 DOMException)。它最初为 null。
FileReader() 构造函数在调用时,必须返回一个新的 FileReader 对象。
readyState 属性的 getter 在调用时,切换 this 的 状态 并运行关联的步骤:
result 属性的 getter 在调用时,必须返回 this 的 结果。
error 属性的 getter 在调用时,必须返回 this 的 错误。
FileReader fr 具有一个关联的 读取操作(read operation) 算法,给定 blob、一个 type 和一个可选的 encodingName,运行以下步骤:-
如果 fr 的 状态 为
"loading",则抛出InvalidStateErrorDOMException。 -
将 fr 的 状态 设置为
"loading"。 -
将 fr 的 结果 设置为
null。 -
将 fr 的 错误 设置为
null。 -
令 stream 为对 blob 调用 获取流 的结果。
-
令 reader 为从 stream 获取读取器 的结果。
-
令 bytes 为一个空 字节序列。
-
令 chunkPromise 为使用 reader 从 stream 读取数据块(reading a chunk) 的结果。
-
令 isFirstChunk 为 true。
-
并行(In parallel) 执行,当 true 时:
-
等待 chunkPromise 被履行或拒绝。
-
如果 chunkPromise 被履行,且 isFirstChunk 为 true,则 排队一个任务,以在 fr 上 触发一个名为
loadstart的进度事件。我们可能会将
loadstart修改为同步分发,以与 XMLHttpRequest 的行为保持一致。 [Issue #119] -
将 isFirstChunk 设置为 false。
-
如果 chunkPromise 被兑现(fulfilled)为一个对象,且该对象的
done属性为 false,value属性为一个Uint8Array对象,则执行以下步骤 -
否则,如果 chunkPromise 被兑现为一个
done属性为 true 的对象,则 排入一个任务 以运行以下步骤并终止此算法 -
否则,如果 chunkPromise 被以错误 error 拒绝(rejected),则 排入一个任务 以运行以下步骤并终止此算法
-
将 文件读取任务源 用于所有这些任务。
6.2.1. 事件处理程序内容属性
以下是用户代理必须在 FileReader 上作为 DOM 属性支持的 事件处理程序内容属性(及其对应的 事件处理程序事件类型)
| event handler content attribute | 事件处理器事件类型 |
|---|---|
onloadstart
| loadstart
|
onprogress
| progress
|
onabort
| abort (中止)
|
onerror
| error
|
onload
| load
|
onloadend
| loadend
|
6.2.2. FileReader 状态
FileReader 对象可以处于 3 种状态之一。readyState 属性用于告知对象当前所处的状态。EMPTY(数值 0)-
FileReader对象已被构造,且没有挂起的读取操作。尚未调用任何 读取方法。这是新创建的FileReader对象的默认状态,直到在其上调用了任一 读取方法。 LOADING(数值 1)DONE(数值 2)-
整个
File或Blob已被读取到内存中,或者发生了 文件读取错误,或者已使用abort()中止了读取。FileReader不再读取File或Blob。如果readyState设置为DONE,则意味着已在此FileReader上调用了至少一个 读取方法。
6.2.3. 读取 File 或 Blob
FileReader 接口提供了几种 异步读取方法——readAsArrayBuffer()、readAsBinaryString()、readAsText() 和 readAsDataURL(),它们将文件读入内存。
注:如果同一 FileReader 对象上调用了多个并发读取方法,用户代理会在 readyState = LOADING 时调用的任何读取方法上抛出 InvalidStateError。
(FileReaderSync 提供了几种 同步读取方法。统称为 FileReader 和 FileReaderSync 的同步和异步读取方法为 读取方法。)
6.2.3.1. readAsDataURL() 方法
readAsDataURL(blob) 方法在被调用时,必须针对 blob 发起一个带有 DataURL 的 读取操作。
6.2.3.2. readAsText() 方法
readAsText(blob, encoding) 方法在被调用时,必须针对 blob 发起一个带有 Text 和 encoding 的 读取操作。
6.2.3.3. readAsArrayBuffer() 方法
readAsArrayBuffer(blob) 方法在被调用时,必须针对 blob 发起一个带有 ArrayBuffer 的 读取操作。
6.2.3.4. readAsBinaryString() 方法
readAsBinaryString(blob) 方法在被调用时,必须针对 blob 发起一个带有 BinaryString 的 读取操作。
注:推荐使用 readAsArrayBuffer(),而非为向后兼容提供的 readAsBinaryString()。
6.2.3.5. abort() 方法
当调用 abort() 方法时,用户代理必须运行以下步骤
6.3. 打包数据
Blob 有一个关联的 打包数据 算法,给定 bytes、一个 type、一个可选的 mimeType 和一个可选的 encodingName,该算法会根据 type 切换并运行相关步骤- DataURL
-
将 bytes 作为 DataURL [RFC2397] 返回,受以下考量因素约束
-
如果 mimeType 可用,则按照 Data URL 规范 [RFC2397] 将其作为 Data URL 的一部分使用。
-
如果 mimeType 不可用,则返回一个不含媒体类型的 Data URL。[RFC2397]
更好地指定 DataURL 的生成方式。[Issue #104]
-
- 文本
- ArrayBuffer
-
返回一个新的内容为 bytes 的
ArrayBuffer。 - BinaryString
-
将 bytes 作为二进制字符串返回,其中每个字节由等值的代码单元 [0..255] 表示。
6.4. 事件
FileReader 对象必须是本规范中所有事件的事件目标。
当本规范提到 称为 e 的 触发进度事件 时(针对给定的 FileReader reader 上的某个 ProgressEvent e),以下内容具有规范性
6.4.1. 事件摘要
以下是 触发 在 FileReader 对象上的事件。
| 事件名称 | Interface | 触发于…… |
|---|---|---|
loadstart
| ProgressEvent
| 读取开始时。 |
progress
| ProgressEvent
| 读取(和解码)blob 的过程中 |
abort (中止)
| ProgressEvent
| 读取已中止时。例如,通过调用 abort() 方法。 |
error
| ProgressEvent
| 读取失败时(参见 文件读取错误)。 |
load
| ProgressEvent
| 读取成功完成时。 |
loadend
| ProgressEvent
| 请求已完成时(无论是成功还是失败)。 |
6.4.2. 事件不变量摘要
本节是资料性的。
以下是不变量,适用于本规范中给定的异步 读取方法 的 事件触发
-
一旦触发了
loadstart,相应的loadend就会在读取完成时触发,除非满足以下任何条件注:事件
loadstart和loadend并非一一对应。此示例展示了“读取链”:在“第一次”读取继续处理的同时,从事件处理程序内部启动另一次读取。// In code of the sort... reader. readAsText( file); reader. onload= function (){ reader. readAsText( alternateFile);} ..... //... the loadend event must not fire for the first read reader. readAsText( file); reader. abort(); reader. onabort= function (){ reader. readAsText( updatedFile);} //... the loadend event must not fire for the first read -
当
blob被完全读入内存时,将触发一个progress事件。 -
在
abort、load和error中的任何一个触发后,不会触发任何progress事件。对于给定的读取,abort、load和error中最多触发一个。
6.5. 在线程上读取
Web Workers 允许使用同步的 File 或 Blob 读取 API,因为在线程上进行此类读取不会阻塞主线程。本节定义了一个同步 API,可以在 Workers [Workers] 内使用。Workers 可以同时利用异步 API(FileReader 对象)和 同步 API(FileReaderSync 对象)。
6.5.1. FileReaderSync API
此接口提供的方法可用于 同步读取 File 或 Blob 对象到内存中。
[Exposed =(DedicatedWorker ,SharedWorker )]interface {FileReaderSync (); // Synchronously return stringsconstructor ArrayBuffer readAsArrayBuffer (Blob );blob DOMString readAsBinaryString (Blob );blob DOMString readAsText (Blob ,blob optional DOMString );encoding DOMString readAsDataURL (Blob ); };blob
6.5.1.1. 构造函数
当调用 FileReaderSync() 构造函数时,用户代理必须返回一个新的 FileReaderSync 对象。
6.5.1.2. readAsText()
readAsText(blob, encoding) 方法在被调用时,必须运行这些步骤
-
令 stream 为在 blob 上调用 get stream 的结果。
-
令 reader 为从 stream 中 获取读取器 的结果。
-
令 promise 为使用 reader 从 stream 中 读取所有字节 的结果。
-
等待 promise 被兑现或拒绝。
-
如果 promise 以 字节序列 bytes 被兑现
-
抛出 promise 的拒绝原因。
6.5.1.3. readAsDataURL() 方法
readAsDataURL(blob) 方法在被调用时,必须运行这些步骤
-
令 stream 为在 blob 上调用 get stream 的结果。
-
令 reader 为从 stream 中 获取读取器 的结果。
-
令 promise 为使用 reader 从 stream 中 读取所有字节 的结果。
-
等待 promise 被兑现或拒绝。
-
如果 promise 以 字节序列 bytes 被兑现
-
抛出 promise 的拒绝原因。
6.5.1.4. readAsArrayBuffer() 方法
readAsArrayBuffer(blob) 方法在被调用时,必须运行这些步骤
-
令 stream 为在 blob 上调用 get stream 的结果。
-
令 reader 为从 stream 中 获取读取器 的结果。
-
令 promise 为使用 reader 从 stream 中 读取所有字节 的结果。
-
等待 promise 被兑现或拒绝。
-
如果 promise 以 字节序列 bytes 被兑现
-
抛出 promise 的拒绝原因。
6.5.1.5. readAsBinaryString() 方法
readAsBinaryString(blob) 方法在被调用时,必须运行这些步骤
-
令 stream 为在 blob 上调用 get stream 的结果。
-
令 reader 为从 stream 中 获取读取器 的结果。
-
令 promise 为使用 reader 从 stream 中 读取所有字节 的结果。
-
等待 promise 被兑现或拒绝。
-
如果 promise 以 字节序列 bytes 被兑现
-
抛出 promise 的拒绝原因。
注:推荐使用 readAsArrayBuffer(),而非为向后兼容提供的 readAsBinaryString()。
7. 错误和异常
文件读取错误 可能在从底层文件系统读取文件时发生。下面列出的潜在错误状况仅供 参考。
-
正在访问的
File或Blob在调用 异步读取方法 或 同步读取方法 时可能不存在。这可能是因为它在获取引用后被移动或删除(例如与其他应用程序并发修改)。参见NotFoundError。 -
File或Blob可能不可读。这可能是由于在获取File或Blob的引用后出现了权限问题(例如与其他应用程序并发锁定)。此外,快照状态 可能已更改。参见NotReadableError。 -
用户代理可能确定某些文件在 Web 应用程序中使用是不安全的。文件自最初选择后可能已在磁盘上发生变化,从而导致读取无效。此外,一些文件和目录结构可能被底层文件系统视为受限;尝试从中读取可能被视为违反安全规定。参见 § 9 安全与隐私考量 和
SecurityError。
7.1. 抛出异常或返回错误
本节是规范性的。
读取操作 可能因读取 File 或 Blob 时的错误状况而终止;导致 get stream 算法失败的具体错误状况称为 失败原因。失败原因 包括 NotFound、UnsafeFile、TooManyReads、SnapshotState 或 FileLock。
如果由于特定的 失败原因 导致了错误,同步读取方法会 抛出 下表中对应类型的异常。
异步读取方法使用 error 对象的 FileReader 属性,如果由于特定的 失败原因 导致了错误,该属性必须返回下表中类型最适当的 DOMException 对象,否则返回 null。
| 类型 | 描述和失败原因 |
|---|---|
NotFoundError
| 如果在处理读取时无法找到 File 或 Blob 资源,则这是 NotFound 失败原因。对于异步读取方法, |
SecurityError
| If
对于异步读取方法, 这是一个安全错误,用于未涵盖在任何其他 失败原因 中的情况。 |
NotReadableError
| If
对于异步读取方法, |
8. 用于 Blob 和 MediaSource 引用的 URL
本节定义了一种用于引用 Blob 和 MediaSource 对象的 URL 的 方案(scheme)。
8.1. 引言
本节是资料性的。
Blob(或对象)URL 是类似 blob:http://example.com/550e8400-e29b-41d4-a716-446655440000 的 URL。这使得 Blob 和 MediaSource 可以与其他仅被设计为配合 URL 使用的 API(例如 img 元素)集成。Blob URL 也可用于导航以及触发本地生成数据的下载。
为此,URL 接口上暴露了两个静态方法:createObjectURL(obj) 和 revokeObjectURL(url)。第一个方法创建了从 URL 到 Blob 的映射,第二个方法则撤销该映射。只要映射存在,Blob 就无法被垃圾回收,因此需要确保在引用不再需要时尽快撤销 URL。当创建 URL 的全局对象消失时,所有 URL 都会被撤销。
8.2. 模型
每个用户代理必须维护一个 blob URL 存储。blob URL 存储 是一个 映射,其中 键 是 有效的 URL 字符串,值 是 blob URL 条目。
一个 blob URL 条目 由一个 对象(类型为 Blob 或 MediaSource)和一个 环境(一个 环境设置对象)组成。
注:规范必须使用 获取 blob 对象 算法来访问 blob URL 条目 的 对象。
blob URL 存储 中的 键(也称为 blob URL)是 有效的 URL 字符串,当被 解析 时,会得到一个 URL,其 方案 为 "blob",主机名为空,且 路径 由一个本身也是 有效 URL 字符串 的元素组成。
top-level-navigation” 或 “top-level-self-fetch” 环境,获取 blob 对象,请执行以下步骤。它们返回一个 对象。-
令 isAuthorized 为 true。
-
如果 environment 是一个 环境设置对象,则将 isAuthorized 设置为使用 blobUrlEntry 和 environment 检查同分区 blob URL 使用情况 的结果。
-
如果 isAuthorized 为 false,则返回失败。
-
返回 blobUrlEntry 的 对象。
-
令 store 为用户代理的 blob URL 存储。
-
令 url 为 生成一个新的 blob URL 的结果。
-
令 entry 为由 object 和 当前设置对象 组成的新 blob URL 条目。
-
设置 store[url] 为 entry。
-
返回 url。
-
令 store 为用户代理的 blob URL 存储;
-
令 url string 为 序列化 url 的结果。
-
移除 store[url string]。
8.3. blob URL 的解引用模型
-
令 store 为用户代理的 blob URL 存储。
-
令 url string 为 序列化 url(设置 排除片段标志)的结果。
-
如果 store[url string] 存在,则返回 store[url string];否则返回失败。
blob URL 的解析和获取模型的进一步要求定义在 [URL] 和 [Fetch] 规范中。
8.3.1. blob URL 的源
本节是资料性的。
只要 blob URL 尚未被撤销,其源始终与创建该 URL 的环境相同。这是通过 [URL] 规范在解析 URL 时查找 blob URL 存储 中的 URL,并使用该条目返回正确的源来实现的。
如果 URL 被撤销,源的序列化仍将保持与创建该 blob URL 的环境的源的序列化相同,但对于不透明(opaque)源,源本身可能会有所不同。不过这种差异是不可观察的,因为撤销后的 blob URL 无论如何都无法再被解析/获取。
8.3.2. blob URL 的访问限制
Blob URL 只能从 存储键 与创建该 blob URL 的环境匹配的环境中获取。Blob URL 导航不受此限制。
-
令 blobStorageKey 为使用 blobUrlEntry 的 环境 获取非存储用途的存储键 的结果。
-
令 environmentStorageKey 为使用 environment 获取非存储用途的存储键 的结果。
-
如果 blobStorageKey 与 environmentStorageKey 不相等,则返回 false。
-
返回 true。
8.3.3. blob URL 的生命周期
本规范通过以下步骤扩展了 卸载文档清理步骤
-
令 store 为用户代理的 blob URL 存储;
8.4. 创建和撤销 blob URL
Blob URL 是使用 URL 对象上暴露的静态方法创建和撤销的。撤销 blob URL 会解除 blob URL 与其所指资源之间的关联,如果在此之后进行解引用,用户代理必须表现得就像发生了 网络错误 一样。本节描述了对 URL 规范 [URL] 的补充接口,并介绍了 blob URL 的创建和撤销方法。
[Exposed =(Window ,DedicatedWorker ,SharedWorker )]partial interface URL {static DOMString createObjectURL ((Blob or MediaSource ));obj static undefined revokeObjectURL (DOMString ); };url
createObjectURL(obj) 静态方法必须返回 向 blob URL 存储中添加条目 以获取 obj 的结果。revokeObjectURL(url) 静态方法必须运行这些步骤-
令 urlRecord 为 解析 url 的结果。
-
如果 urlRecord 的 方案 不为 "
blob",则返回。 -
令 entry 为 urlRecord 的 blob URL 条目。
-
如果 entry 为 null,则返回。
-
令 isAuthorized 为使用 entry 和 当前设置对象 检查同分区 blob URL 使用情况 的结果。
-
如果 isAuthorized 为 false,则返回。
-
从 Blob URL 存储中移除条目 以获取 url。
注:这意味着尝试撤销一个未注册或从不同存储分区的环境注册的 URL 将静默失败,而不是抛出某种错误。如果发生这种情况,用户代理可能会在错误控制台中显示消息。
注:在 URL 被撤销后尝试对其进行解引用将导致 网络错误。在 url 被撤销之前启动的请求仍应成功。
window1 和 window2 是独立的,但在 同源 中;window2 可以是 window1 内部的一个 iframe。myurl= window1. URL. createObjectURL( myblob); window2. URL. revokeObjectURL( myurl);
由于用户代理拥有一个全局的 blob URL 存储,因此可以从与创建时不同的窗口撤销对象 URL。URL. 调用确保后续对 revokeObjectURL()myurl 的解引用导致用户代理表现得就像发生了 网络错误 一样。
8.4.1. blob URL 创建和撤销示例
Blob URL 是用于 获取 Blob 对象的字符串,并且只要它们是从中通过 URL. 铸造的 createObjectURL()document 存在,它们就可以保持存在——参见 § 8.3.3 blob URL 的生命周期。
本节给出了 blob URL 创建和撤销的使用示例及说明。
img 元素 [HTML] 引用了相同的 blob URLurl= URL. createObjectURL( blob); img1. src= url; img2. src= url;
URL.revokeObjectURL()。var blobURLref= URL. createObjectURL( file); img1= new Image(); img2= new Image(); // Both assignments below work as expected img1. src= blobURLref; img2. src= blobURLref; // ... Following body load // Check if both images have loaded if ( img1. complete&& img2. complete) { // Ensure that subsequent refs throw an exception URL. revokeObjectURL( blobURLref); } else { msg( "Images cannot be previewed!" ); // revoke the string-based reference URL. revokeObjectURL( blobURLref); }
上述示例允许对单个 blob URL 进行多次引用,并且 Web 开发人员在两个图像对象加载完成后撤销该 blob URL 字符串。虽然不限制 blob URL 的使用次数提供了更大的灵活性,但它增加了泄漏的可能性;开发人员应将其与 URL. 的相应调用配对使用。revokeObjectURL()
9. 安全与隐私考量
本节是资料性的。
本规范允许 Web 内容读取底层文件系统中的文件,并提供了一种通过唯一标识符访问文件的方法,因此需要考虑一些安全问题。本规范还假定主要的用户交互是通过 HTML 表单的 <input type="file"/> 元素 [HTML] 进行的,并且所有由 FileReader 对象读取的文件均已先由用户选择。重要的安全考量包括防止恶意文件选择攻击(选择循环)、防止对系统敏感文件的访问,以及防范文件在被选中后在磁盘上被篡改。
- 防止选择循环
-
在文件选择期间,用户可能会被与
<input type="file"/>相关联的文件选择器轰炸(处于一种强制在关闭文件选择器前进行选择的“必须选择”循环中),用户代理可以通过使返回的FileList对象大小为 0 来防止对任何选定内容的文件访问。 - 系统敏感文件
-
(例如 /usr/bin 中的文件、密码文件以及其他原生操作系统可执行文件)通常不应暴露给 Web 内容,也不应通过 blob URL 访问。对于同步读取方法,用户代理可以 抛出
SecurityError异常;对于异步读取,则可以返回SecurityError异常。
10. 需求与用例
本节涵盖了此 API 的需求,并阐述了一些用例。此版本的 API 未能满足所有用例;后续版本可能会选择解决这些问题。
-
一旦用户授予权限,用户代理应提供以编程方式直接从本地文件读取和解析数据的能力。
-
数据应能存储在本地以供日后使用,这对 Web 应用的离线数据访问非常有用。
-
用户代理应提供在给定数据量和文件名的情况下,以编程方式保存本地文件的能力。
注意: 虽然本规范没有提供触发下载的显式 API 调用,但 HTML5 规范已经解决了这个问题。
download属性的a元素会启动下载,以指定名称保存一个File。此 API 与a元素上的download属性的组合,允许在 Web 应用内创建文件,并能够将其保存在本地。 -
用户代理应提供一种简化的、以编程方式将数据从文件发送到远程服务器的能力,该能力应比当今基于表单的上传更高效。
-
用户代理应提供一个暴露给脚本的 API 来实现上述功能。任何与文件系统的交互都会通过 UI 通知用户,给予用户完全取消或中止交易的能力。用户会收到任何文件选择的通知,并可以取消这些选择。在没有用户干预的情况下,不会静默调用这些 API。
致谢
本规范最初由 SVG 工作组开发。非常感谢 Mark Baker 和 Anne van Kesteren 的反馈。
感谢 Robin Berjon、Jonas Sicking 和 Vsevolod Shmyroff 编辑了最初的规范。
特别感谢 Olli Pettay、Nikunj Mehta、Garrett Smith、Aaron Boodman、Michael Nordman、Jian Li、Dmitry Titov、Ian Hickson、Darin Fisher、Sam Weinig、Adrian Bateman 和 Julian Reschke。
感谢 W3C WebApps WG 以及 public-webapps@w3.org 邮件列表的参与者。