文件 API

W3C 工作草案

关于此文档的更多详情
此版本:
https://www.w3.org/TR/2024/WD-FileAPI-20241204/
最新发布版本:
https://www.w3.org/TR/FileAPI/
编辑草案:
https://w3c.github.io/FileAPI/
先前版本:
历史:
https://www.w3.org/standards/history/FileAPI/
反馈:
GitHub
规范内联
编辑:
(Google)
前任编辑:
Arun Ranganathan (Mozilla Corporation)
测试:
web-platform-tests FileAPI/ (进行中的工作)

摘要

该规范提供了一个用于在 Web 应用中表示文件对象的 API, 以及以编程方式选择它们并访问其数据的功能。这包括:

此外,本规范定义了用于线程化 Web 应用中的对象,以同步读取文件。

§ 10 需求和使用案例 介绍了该规范背后的动机。

该 API 旨在与 Web 平台上的其他 API 和元素配合使用,尤其是: XMLHttpRequest (例如,send() 方法可以重载,以支持 FileBlob 参数),postMessage()DataTransfer (HTML 标准中的拖放 API [HTML]) 和 Web Workers。 另外,还可以通过编程方式从 input 元素获取文件列表, 当它处于 文件上传 状态时 [HTML]。 这些行为在相应的附属规范中进行了定义。

文档状态

本节描述了该文档在发布时的状态。当前 W3C 出版物列表及本技术报告的最新修订版可在 W3C 技术报告索引 中找到,网址为:https://www.w3.org/TR/。

此文档由 Web 应用工作组 作为工作草案发布。此文档旨在成为 W3C 推荐标准。

之前关于本规范的讨论已在两个邮件列表上进行:public-webapps@w3.org (存档) 和 public-webapi@w3.org (存档)。后续讨论将会在 public-webapps@w3.org 邮件列表上进行。

本草案包含对先前最后呼叫工作草案的修改。请将意见发送至 public-webapi@w3.org,如上所述。您可以在 W3C Wiki 上查看最后呼叫反馈:https://www.w3.org/wiki/Webapps/LCWD-FileAPI-20130912

实现报告 是从测试套件中自动生成的。

本文档由 Web 应用工作组作为工作草案采用推荐标准流程发布。 欢迎对本规范提供反馈和意见。请使用 GitHub issues。历史讨论可以在 public-webapps@w3.org 存档中找到。

作为工作草案发布并不意味着 W3C 及其成员的认可。这是一份草案文件,并且可能随时被其他文件更新、替换或废弃。将本文档作为正在进行的工作以外的任何形式引用都是不合适的。

本文档由一个在 W3C 专利政策下运作的小组制定。W3C 维护着一份与该小组交付成果相关的任何专利披露的公开列表;该页面还包含披露专利的说明。任何知晓某项专利并认为其包含必要权利要求的个人必须根据 W3C 专利政策第 6 节披露该信息。

本文档受 2023年11月3日 W3C 流程文档的约束。

1. 简介

本节为说明性内容。

Web 应用程序应具备操作尽可能多的用户输入类型的能力,包括用户可能希望上传到远程服务器或在富 Web 应用程序中操作的文件。 本规范定义了文件的基本表示、文件列表、文件访问产生的错误以及读取文件的编程方式。 此外,本规范还定义了一个表示“原始数据”的接口,该接口可以在符合标准的用户代理的主线程上异步处理。 本规范中定义的接口和 API 可与 Web 平台上公开的其他接口和 API 一起使用。

File 接口代表通常从底层文件系统获取的文件数据, Blob 接口 (“二进制大对象” - 最初由 Google Gears 引入到 Web API 中) 表示不可变的原始数据。FileBlob 的读取应在主线程上异步进行, 线程化 Web 应用程序中可使用可选的同步 API。 使用异步 API 读取文件可防止阻塞并避免用户代理的主线程“冻结”界面。 本规范定义了基于 事件模型 的异步 API,用于读取和访问 FileBlob 的数据。 FileReader 对象提供异步读取方法以访问文件的数据, 通过事件处理器内容属性和事件的触发来进行读取。 使用事件和事件处理器允许不同的代码块监视 读取进度(这在远程驱动器或挂载的驱动器中尤为有用,因为文件访问性能可能与本地驱动器不同), 以及在读取文件时可能出现的错误情况。 下面的示例将更具说明性。

在下面的示例中,不同的代码块处理进度、错误和成功条件。
function startRead() {
  // 通过 DOM 获取 input 元素

  var file = document.getElementById('file').files[0];
  if(file){
    getAsText(file);
  }
}

function getAsText(readFile) {

  var reader = new FileReader();

  // 将文件作为 UTF-16 读取到内存中
  reader.readAsText(readFile, "UTF-16");

  // 处理进度、成功和错误
  reader.onprogress = updateProgress;
  reader.onload = loaded;
  reader.onerror = errorHandler;
}

function updateProgress(evt) {
  if (evt.lengthComputable) {
    // evt.loaded 和 evt.total 是 ProgressEvent 属性
    var loaded = (evt.loaded / evt.total);
    if (loaded < 1) {
      // 增加进度条长度
      // style.width = (loaded * 200) + "px";
    }
  }
}

function loaded(evt) {
  // 获取读取的文件数据
  var fileString = evt.target.result;
  // 处理 UTF-16 文件转储
  if(utils.regexp.isChinese(fileString)) {
    // 中文字符 + 名称验证
  }
  else {
    // 运行其他字符集测试
  }
  // xhr.send(fileString)
}

function errorHandler(evt) {
  if(evt.target.error.name == "NotReadableError") {
    // 无法读取文件
  }
}

2. 术语和算法

当本规范提到 终止算法 时,用户代理必须在完成当前步骤后终止该算法。 本规范中定义的异步 读取方法 可能在相关算法终止之前返回, 并且可以通过调用 abort() 来终止。

本规范中的算法和步骤使用以下数学运算:

本规范使用的术语 Unix 纪元 指的是 1970 年 1 月 1 日 00:00:00 UTC 的时间 (或 1970-01-01T00:00:00Z ISO 8601); 这是 ECMA-262 中概念上 "0" 的时间 [ECMA-262]

分割 Blob 算法 给定 Blob blobstartendcontentType, 用于执行以下步骤,并返回一个新的 Blob,包含从 start 参数到(但不包括)end 参数的字节。算法步骤如下:
  1. originalSizeblob大小

  2. start 参数(如果非空)表示 分割 Blob 调用的起始点, 必须将其视为字节顺序位置,第零位表示第一个字节。用户代理必须按照以下步骤规范化 start

    1. 如果 start 为 null,令 relativeStart 为 0。
    2. 如果 start 为负数,令 relativeStartmax((originalSize + start), 0)
    3. 否则,令 relativeStartmin(start, originalSize)
  3. end 参数(如果非空)表示 分割 Blob 调用的结束点。用户代理必须按照以下步骤规范化 end

    1. 如果 end 为 null,令 relativeEndoriginalSize
    2. 如果 end 为负数,令 relativeEndmax((originalSize + end), 0)
    3. 否则,令 relativeEndmin(end, originalSize)
  4. contentType 参数(如果非空),用于设置表示 Blob 媒体类型的 ASCII 编码小写字符串。 用户代理必须按照以下步骤规范化 contentType

    1. 如果 contentType 为 null,令 relativeContentType 为空字符串。
    2. 否则,令 relativeContentTypecontentType,并执行以下子步骤:
      1. 如果 relativeContentType 包含 U+0020 到 U+007E 之外的任何字符,则将 relativeContentType 设为空字符串,并退出这些子步骤。

      2. relativeContentType 中的每个字符转换为 ASCII 小写字母

  5. spanmax((relativeEnd - relativeStart), 0)

  6. 返回一个新的 Blob 对象 S,其特征如下:

    1. S 指的是从 blob 关联的 字节 序列中, 从 relativeStart 字节顺序位置开始的 span 个连续字节。
    2. S.大小 = span
    3. S.类型 = relativeContentType

3. Blob 接口与二进制数据

Blob 对象 指的是一个 字节 序列, 并且有一个 大小 属性,表示字节序列中的字节总数, 以及一个 类型 属性, 这是一个小写的 ASCII 编码字符串,表示该 字节 序列的媒体类型。

每个 Blob 必须 有一个内部 快照状态, 如果存在底层存储,则该状态必须最初设置为底层存储的状态。 有关 快照状态 的进一步规范定义可以在 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 chunks
  Blob slice(optional [Clamp] long long start,
            optional [Clamp] long long end,
            optional DOMString contentType);

  // read from the Blob.
  [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 对象 是 可序列化对象。它们的 序列化步骤, 给定 valueserialized,如下:

  1. serialized.[[SnapshotState]] 设置为 value快照状态

  2. serialized.[[ByteSequence]] 设置为 value 的底层字节序列。

它们的 反序列化步骤,给定 serializedvalue,如下:

  1. value快照状态 设置为 serialized.[[SnapshotState]]。

  2. value 的底层字节序列设置为 serialized.[[ByteSequence]]。

一个 Blob blob 具有关联的 获取流 算法, 执行以下步骤:
  1. stream 为在 blob相关 Realm 中创建的一个 新的 ReadableStream

  2. 设置 stream 以支持字节读取。

  3. 执行以下步骤 并行

    1. 当尚未读取 blob 的所有字节时:

      1. bytes 为从 blob 读取的 所产生的字节序列, 如果无法读取块,则为失败。

      2. blob相关全局对象 上,基于 全局任务队列 来执行以下步骤:

        1. 如果 bytes 读取失败,则使用 错误失败原因 标记 stream 为错误,并中止这些步骤。

        2. chunk 为一个新的 Uint8Array 包装一个包含 bytesArrayBuffer。如果创建 ArrayBuffer 时抛出异常,则将该异常标记为 错误 并中止这些步骤。

        3. 入队 chunkstream

      我们需要更具体地说明从 Blob 读取实际执行的操作, 可能出现的错误,以及块大小等细节。

  4. 返回 stream

3.1. 构造函数

Blob() 构造函数可以使用零个或多个参数进行调用。 当 Blob() 构造函数被调用时, 用户代理必须执行以下步骤:
  1. 如果调用时不带参数, 返回一个新的 Blob 对象,该对象由 0 字节组成, 大小 设置为 0, 并且 类型 设置为空字符串。

  2. bytes 为根据 blobPartsoptions 执行 处理 blob 部分 的结果。

  3. 如果 类型 参数的 options 字典成员不为空字符串, 则执行以下子步骤:

    1. t类型 字典成员。 如果 t 包含 U+0020 到 U+007E 之外的任何字符, 则将 t 设为空字符串,并退出这些子步骤。

    2. t 中的每个字符转换为 ASCII 小写

  4. 返回一个 Blob 对象,该对象将 bytes 作为其关联的 字节 序列, 其 大小 设置为 bytes 的长度, 并且其 类型 设置为上述子步骤中的 t 的值。

3.1.1. 构造函数参数

Blob() 构造函数可以使用以下参数进行调用:

blobParts 序列
它可以包含以下任意类型的元素,并且顺序不限:
一个 可选的 BlobPropertyBag
它包含以下可选成员:
给定一个 BlobPart 的序列 partsBlobPropertyBag options,执行以下步骤以 处理 blob 部分
  1. bytes 为一个空的字节序列。

  2. parts 中的每个 element 执行以下操作:

    1. 如果 elementUSVString, 执行以下子步骤:

      1. selement

      2. 如果 endings 成员的 options"native", 将 s 设置为 转换为本地行结束符 的结果。

      3. UTF-8 编码 s 的结果附加到 bytes

        注意: WebIDL 的算法 [WebIDL] 会将无效的 utf-16 字符串中的未匹配替代字符替换为 U+FFFD 替代字符。 在某些情况下,Blob 构造函数可能会导致某些数据丢失 ,这是由于字符序列丢失或混乱造成的。

    2. 如果 elementBufferSource获取缓冲区源持有的字节副本, 并将这些字节附加到 bytes

    3. 如果 elementBlob, 将其表示的字节附加到 bytes

      注意: 类型 数组元素的类型将被忽略,并且不会影响返回的 Blob 对象的 类型

  3. 返回 bytes

要将 行结束符转换为本地格式,给定 字符串 s, 执行以下步骤:
  1. native line ending代码点 U+000A LF。

  2. 如果底层平台的约定是将换行符表示为回车和换行序列, 则将 native line ending 设置为 代码点 U+000D CR 后跟 代码点 U+000A LF。

  3. result 设置为空的 字符串

  4. position位置变量,最初指向 s 的起始位置。

  5. token收集一系列代码点的结果, 这些代码点不等于 U+000A LF 或 U+000D CR,s 中的 position 被提供给它。

  6. token 附加到 result

  7. position 尚未超过 s 的结尾时:

    1. 如果 position 处的 代码点 等于 U+000D CR:

      1. native line ending 附加到 result

      2. position 前进 1。

      3. 如果 position 未超过 s 的结尾,并且 position 处的代码点等于 U+000A LF, 则将 position 前进 1。

    2. 否则,如果 position 处的代码点等于 U+000A LF, 则将 position 前进 1,并将 native line ending 附加到 result

    3. token收集一系列代码点 的结果, 这些代码点不等于 U+000A LF 或 U+000D CR,s 中的 position 被提供给它。

    4. token 附加到 result

  8. 返回 result

以下是构造函数使用的示例。
// 创建一个新的 Blob 对象

var a = new Blob();

// 创建一个 1024 字节的 ArrayBuffer
// buffer 也可以来自读取一个文件

var buffer = new ArrayBuffer(1024);

// 基于 buffer 创建 ArrayBufferView 对象

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, 只读
返回字节序列的大小(以字节为单位)。 获取时,符合标准的用户代理必须返回可以通过 FileReaderFileReaderSync 对象读取的字节总数, 如果 Blob 没有可读取的字节,则返回 0。
type, 类型为 DOMString, 只读
以小写形式返回表示 Blob 的媒体类型的 ASCII 编码字符串。 获取时,用户代理必须以小写形式返回 Blob 的类型作为 ASCII 编码字符串, 使其转换为 字节序列时, 它是可解析的 MIME 类型, 如果无法确定类型,则为空字符串(0 字节)。

type 属性可以通过构造函数调用或 slice() 调用由 Web 应用程序自行设置; 在这些情况下,此属性的进一步规范条件请参阅 § 3.1 构造函数§ 4.1 构造函数§ 3.3.1 slice() 方法。 用户代理还可以确定 typeBlob, 特别是当字节序列来自磁盘文件时; 在这种情况下,进一步的规范条件请参阅 文件类型指南

注意: 如果将表示 Blob 对象类型的 ASCII 编码字符串转换为字节序列时, 执行 解析 MIME 类型 算法没有返回失败, 则 t 类型的 Blob 被认为是 可解析的 MIME 类型

注意: 使用 type 属性会影响 打包数据 算法 ,并决定在 抓取 Blob URL 时的 Content-Type 头。

3.3. 方法和参数

3.3.1. slice() 方法

slice() 方法 返回一个新的 Blob 对象,字节范围从可选的 start 参数开始, 到(但不包括)可选的 end 参数,并且具有一个 type 属性,该属性的值是可选的 contentType 参数。其操作如下:
  1. sliceStartsliceEndsliceContentType 为 null。

  2. 如果给定了 start,将 sliceStart 设置为 start

  3. 如果给定了 end,将 sliceEnd 设置为 end

  4. 如果给定了 contentType,将 sliceContentType 设置为 contentType

  5. 返回执行 slice blob 的结果,参数为 thissliceStartsliceEndsliceContentType

下面的示例说明了可能的不同类型的 slice() 调用。由于 File 接口继承自 Blob 接口,因此示例基于 File 接口的使用。
// 通过 DOM 获取输入元素

var file = document.getElementById('file').files[0];
if(file)
{
  // 创建文件的相同副本
  // 以下两个调用是等价的

  var fileClone = file.slice();
  var fileClone2 = file.slice(0, file.size);

  // 从文件中间开始切片为一半,负数的使用

  var fileChunkFromEnd = file.slice(-(Math.round(file.size/2)));

  // 从文件开头开始切片为一半

  var fileChunkFromStart = file.slice(0, Math.round(file.size/2));

  // 从文件开头开始切片,直到距末尾 150 字节

  var fileNoMetadata = file.slice(0, -150, "application/experimental");
}

3.3.2. stream() 方法

stream() 方法被调用时,必须返回调用 get stream 的结果,调用对象为 this

3.3.3. text() 方法

text() 方法被调用时,必须运行以下步骤:

  1. 调用 get stream 的结果作为 stream,调用对象为 this

  2. 调用 获取读取器 的结果作为 reader,读取自 stream。 如果抛出异常,返回一个以该异常拒绝的新 promise。

  3. promise 作为调用 读取所有字节 的结果,读取自 stream 并使用 reader

  4. 返回 promise 的结果,通过一个履约处理程序,该处理程序返回对其第一个参数运行 UTF-8 解码 的结果。

注意: 这与 readAsText() 的行为不同, 更好地与 Fetch 的 text() 行为对齐。 具体来说,该方法将始终使用 UTF-8 编码,而 FileReader 根据 blob 的类型和传入的编码名称,可能使用不同的编码。

3.3.4. arrayBuffer() 方法

arrayBuffer() 方法被调用时,必须运行以下步骤:

  1. 调用 get stream 的结果作为 stream,调用对象为 this

  2. 调用 获取读取器 的结果作为 reader,读取自 stream。 如果抛出异常,返回一个以该异常拒绝的新 promise。

  3. promise 作为调用 读取所有字节 的结果,读取自 stream 并使用 reader

  4. 返回通过一个履约处理程序转换 promise 的结果,该处理程序返回一个新的 ArrayBuffer,其内容为其第一个参数。

3.3.5. bytes() 方法

bytes() 方法被调用时,必须运行以下步骤:

  1. 调用 get stream 的结果作为 stream,调用对象为 this

  2. 调用 获取读取器 的结果作为 reader,读取自 stream。 如果抛出异常,返回一个以该异常拒绝的新 promise。

  3. promise 作为调用 读取所有字节 的结果,读取自 stream 并使用 reader

  4. 返回通过一个履约处理程序转换 promise 的结果,该处理程序返回一个新的 Uint8Array,其包装了一个包含其第一个参数的 ArrayBuffer

4. File 接口

File 对象是一个 Blob 对象,具有一个 name 属性,这个属性是一个字符串; 它可以通过构造函数在 web 应用程序中创建, 或者是对底层操作系统文件系统中的一个 字节 序列的引用。

如果 File 对象是对磁盘上文件的 字节 序列的引用, 则其 快照状态 应设置为创建该 File 对象时文件在磁盘上的状态。

注意: 这是一个对于用户代理来说实现起来非常复杂的要求, 因此它不是 必须 而是 应该 [RFC2119]。 用户代理应尽力将 File 对象的 快照状态 设置为创建引用时磁盘上存储的状态。 如果文件在引用创建之后被修改, 则该 File快照状态 将与底层存储的状态不一致。 用户代理可以使用修改时间戳和其他机制来维护 快照状态, 但这留作实现细节。

当一个 File 对象引用磁盘上的文件时, 用户代理必须返回该文件的 type, 并且必须遵循以下的 文件类型指南

[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 对象是 可序列化对象。它们的 序列化步骤,给定 valueserialized 时,步骤如下:

  1. serialized.[[SnapshotState]] 设置为 value快照状态

  2. serialized.[[ByteSequence]] 设置为 value 的底层字节序列。

  3. serialized.[[Name]] 设置为 valuename 属性的值。

  4. serialized.[[LastModified]] 设置为 valuelastModified 属性的值。

它们的 反序列化步骤,给定 valueserialized 时,步骤如下:

  1. value快照状态 设置为 serialized 的 [[SnapshotState]]。

  2. value 的底层字节序列设置为 serialized 的 [[ByteSequence]]。

  3. valuename 属性初始化为 serialized 的 [[Name]]。

  4. valuelastModified 属性初始化为 serialized 的 [[LastModified]]。

4.1. 构造函数

当调用 File 构造函数时,可以传入两个或三个参数,取决于是否使用了可选的字典参数。 当调用 File() 构造函数时,用户代理必须执行以下步骤:
  1. 根据 fileBitsoptions,获取 处理 blob 部件 的结果,赋值给 bytes

  2. 将构造函数的 fileName 参数赋值给 n

    注意:不同的底层操作系统文件系统使用不同的文件名约定; 构造文件时,强制使用 UTF-16 可减少文件名转换为 字节 序列时的歧义。

  3. 通过执行以下子步骤处理 FilePropertyBag 字典参数:

    1. 如果提供了 type 成员且不是空字符串, 则将字典成员 type 赋值给 t。 如果 t 包含任何不在 U+0020 到 U+007E 范围内的字符, 则将 t 设为空字符串并从这些子步骤中返回。

    2. t 中的每个字符转换为 ASCII 小写

    3. 如果提供了 lastModified 成员, 则将字典成员 lastModified 赋值给 d。 如果未提供, 则将 d 设为当前日期和时间, 以自 Unix Epoch(相当于 Date.now() [ECMA-262])以来的毫秒数表示。

      注意:由于 ECMA-262 的 Date 对象转换为代表自 Unix Epoch 以来的毫秒数的 long long 值, 因此 lastModified 成员可以是 Date 对象 [ECMA-262]

  4. 返回一个新的 File 对象 F,使得:

    1. F 指向 bytes 字节序列。

    2. Fsize 设置为 bytes 的总字节数。

    3. Fname 设置为 n

    4. Ftype 设置为 t

    5. FlastModified 设置为 d

4.1.1. 构造函数参数

File() 构造函数可以使用以下参数调用:

fileBits sequence
可以按任意顺序包含以下任意数量的元素:
fileName 参数
一个 USVString 参数,表示文件的名称; 该构造函数参数的规范条件可在 § 4.1 构造函数 中找到。
一个可选的 FilePropertyBag 字典
除了 BlobPropertyBag 成员 之外,还接受一个成员:
  • 一个可选的 lastModified 成员, 必须是 long long 类型; 此成员的规范条件在 § 4.1 构造函数 中提供。

4.2. 属性

name, 类型为 DOMString, 只读
文件的名称。 获取时,这必须返回文件的名称,作为字符串。 不同的底层操作系统文件系统使用不同的文件名变体和约定; 这只是文件的名称,不包括路径信息。 获取时,如果用户代理无法提供此信息, 则必须返回空字符串。 如果使用构造函数创建了 File 对象, 有关此属性的更多规范条件请参见 § 4.1 构造函数
lastModified, 类型为 long long, 只读
文件的最后修改日期。 获取时,如果用户代理能够提供此信息, 则必须返回一个 long long,表示文件最后修改时间, 为自 Unix Epoch 以来的毫秒数。 如果最后的修改日期和时间未知, 该属性必须返回当前日期和时间, 以 long long 表示自 Unix Epoch 以来的毫秒数; 这相当于 Date.now() [ECMA-262]。 如果使用构造函数创建了 File 对象, 有关此属性的更多规范条件请参见 § 4.1 构造函数

File 接口在暴露 FileList 类型属性的对象上可用; 这些对象在 HTML 中定义 [HTML]。 继承自 BlobFile 接口是不可变的, 因此表示可以在启动 读取操作 时读取到内存中的文件数据。 用户代理必须将读取时不再存在的文件处理为 错误, 如果在 Web Worker 上使用 FileReaderSync 则抛出 NotFoundError 异常, 或者触发 error 事件, error 属性返回 NotFoundError

以下示例中,文件对象的元数据以有意义的方式显示,并使用名称和最后修改日期创建文件对象。
var file = document.getElementById("filePicker").files[0];
var date = new Date(file.lastModified);
println("您选择的文件是 " + file.name + ",最后修改日期为 " + date.toDateString() + "。");

...

// 生成具有特定最后修改日期的文件

var d = new Date(2013, 12, 5, 16, 23, 45, 600);
var generatedFile = new File(["初稿 ...."], "Draft1.txt", {type: "text/plain", lastModified: d})

...

5. FileList 接口

注意: FileList 接口应被视为“存在风险”, 因为 Web 平台上的普遍趋势是用 Array 对象替代这种接口,正如 ECMAScript 所定义的 [ECMA-262]。 尤其是,类似 filelist.item(0) 的语法存在风险; 大多数其他 FileList 的程序化使用不太可能受到迁移到 Array 类型的影响。

此接口是一个 File 对象的列表。

[Exposed=(Window,Worker), Serializable]
interface FileList {
  getter File? item(unsigned long index);
  readonly attribute unsigned long length;
};

FileList 对象是 可序列化的对象。其 序列化步骤, 给定 valueserialized,如下所示:

  1. serialized.[[Files]] 设置为空的 列表

  2. 对于 value 中的每个 file,将 file子序列化 追加到 serialized.[[Files]]。

反序列化步骤,给定 serializedvalue,如下所示:

  1. 对于 serialized.[[Files]] 中的每个 file,将 file子反序列化 添加到 value 中。

示例使用通常涉及通过 DOM 访问表单中的 <input type="file"> 元素, 然后访问选定的文件。
// uploadData 是表单元素
// fileChooser 是类型为 'file' 的输入元素
var file = document.forms['uploadData']['fileChooser'].files[0];

// 可选语法是
// var file = document.forms['uploadData']['fileChooser'].files.item(0);

if(file)
{
  // 执行文件操作
}

5.1. 属性

length, 类型为 unsigned long, 只读
必须返回 FileList 对象中文件的数量。 如果没有文件,此属性必须返回 0。

5.2. 方法和参数

item(index)
必须返回 index 位置的 File 对象 在 FileList 中。 如果在 FileList 中没有 index 位置的 File 对象, 那么该方法必须返回 null

index 必须被用户代理 视为表示 File 对象在 FileList 中位置的值, 其中 0 表示第一个文件。支持的属性索引 是从零到 小于 File 对象数量的数字范围。 如果没有这样的 File 对象, 那么就没有支持的属性索引。

注意: HTMLInputElement 接口有一个类型为 FileList 的只读属性, 这是上例中访问的内容。 其他具有类型为 FileList 只读属性的接口包括 DataTransfer 接口。

6. 读取数据

6.1. 文件读取任务源

本规范定义了一个新的通用 任务源,称为 文件读取任务源, 用于本规范中排队的所有任务,这些任务用于读取与 BlobFile 对象相关的字节序列。 它用于响应异步读取二进制数据的功能。

6.2. FileReader API

[Exposed=(Window,Worker)]
interface FileReader: EventTarget {
  constructor();
  // async read methods
  undefined readAsArrayBuffer(Blob blob);
  undefined readAsBinaryString(Blob blob);
  undefined readAsText(Blob blob, optional DOMString encoding);
  undefined readAsDataURL(Blob blob);

  undefined abort();

  // states
  const unsigned short EMPTY = 0;
  const unsigned short LOADING = 1;
  const unsigned short DONE = 2;

  readonly attribute unsigned short readyState;

  // File or Blob data
  readonly attribute (DOMString or ArrayBuffer)? result;

  readonly attribute DOMException? error;

  // event handler content attributes
  attribute EventHandler onloadstart;
  attribute EventHandler onprogress;
  attribute EventHandler onload;
  attribute EventHandler onabort;
  attribute EventHandler onerror;
  attribute EventHandler onloadend;
};

一个 FileReader 对象有一个关联的 状态, 可以是 "empty""loading",或者 "done"。初始状态为 "empty"

一个 FileReader 对象有一个关联的 结果null,一个 DOMStringArrayBuffer)。 初始值为 null

一个 FileReader 对象有一个关联的 错误nullDOMException)。 初始值为 null

FileReader() 构造函数在调用时,必须返回一个新的 FileReader 对象。

readyState 属性的 getter 在调用时,基于 this状态,并执行相应的步骤:

"empty"

返回 EMPTY

"loading"

返回 LOADING

"done"

返回 DONE

result 属性的 getter 在调用时,必须返回 this结果

error 属性的 getter 在调用时,必须返回 this错误

一个 FileReader fr 有一个关联的 读取操作 算法, 该算法在给定 blobtype 和一个可选的 encodingName 的情况下,运行以下步骤:
  1. 如果 fr状态"loading", 抛出一个 InvalidStateErrorDOMException

  2. fr状态 设置为 "loading"

  3. fr结果 设置为 null

  4. fr错误 设置为 null

  5. stream 成为调用 获取流blob 上的结果。

  6. reader 成为 从流中获取 reader 的结果。

  7. bytes 成为空的 字节序列

  8. chunkPromise 成为 从流中读取一个数据块 的结果。

  9. isFirstChunk 为 true。

  10. 并行,在此期间一直循环:

    1. 等待 chunkPromise 被履行或拒绝。

    2. 如果 chunkPromise 被履行,并且 isFirstChunk 为 true,将一个任务加入队列,以 触发一个进度事件,事件名为 loadstart,在 fr 上触发。

      我们 可能会将 loadstart 修改为同步调度, 以对齐 XMLHttpRequest 的行为。[Issue #119]

    3. isFirstChunk 设置为 false。

    4. 如果 chunkPromise 的履行结果为一个对象,且其 done 属性为 false 并且 value 属性为 Uint8Array 对象,运行以下步骤:

      1. bs 成为 字节序列,由 Uint8Array 对象表示。

      2. bs 附加到 bytes

      3. 如果自上次调用这些步骤以来大约已经过去了 50ms,将一个任务加入队列,以 触发一个进度事件,事件名为 progressfr 上触发。

      4. chunkPromise 设置为从 stream 中通过 reader 读取数据块 的结果。

    5. 否则,如果 chunkPromise 的履行结果是一个 done 属性为 true 的对象,将一个任务加入队列,运行以下步骤并中止该算法:

      1. fr状态 设置为 "done"

      2. result 成为调用 封装数据 的结果,给定 bytestypeblob类型encodingName

      3. 如果 封装数据 抛出了异常 error

        1. fr错误 设置为 error

        2. 触发一个进度事件,事件名为 error,在 fr 上触发。

      4. 否则:

        1. fr结果 设置为 result

        2. 触发一个进度事件,事件名为 load,在 fr 上触发。

      5. 如果 fr状态 不是 "loading"触发一个进度事件,事件名为 loadendfr 上触发。

        注意: 处理 loaderror 事件的事件处理器可能已经启动了另一次加载, 如果发生这种情况,则不会触发此加载的 loadend 事件。

    6. 否则,如果 chunkPromise 被拒绝并带有错误 error将一个任务加入队列,运行以下步骤并中止该算法:

      1. fr状态 设置为 "done"

      2. fr错误 设置为 error

      3. 触发一个进度事件,事件名为 errorfr 上触发。

      4. 如果 fr状态 不是 "loading"触发一个进度事件,事件名为 loadendfr 上触发。

        注意: 处理 error 事件的事件处理器可能已经启动了另一次加载, 如果发生这种情况,则不会触发此加载的 loadend 事件。

使用 文件读取任务源 执行所有这些任务。

6.2.1. 事件处理程序内容属性

以下是事件处理程序内容属性(及其对应的事件处理程序事件类型), 用户代理必须在作为 DOM 属性的 FileReader 上支持这些属性:

事件处理程序内容属性 事件处理程序事件类型
onloadstart loadstart
onprogress progress
onabort abort
onerror error
onload load
onloadend loadend

6.2.2. FileReader 状态

FileReader 对象可以处于三种状态之一。 readyState 属性告诉你对象处于哪种状态:
EMPTY (数值 0)

FileReader 对象已被构建, 且没有待处理的读取操作。 任何 读取方法 尚未被调用。 这是新创建的 FileReader 对象的默认状态, 直到调用了其中一个 读取方法

LOADING (数值 1)

正在读取 FileBlob。 其中一个 读取方法 正在处理, 且在读取期间未发生任何错误。

DONE (数值 2)

整个 FileBlob 已被读取到内存中, 或发生了 文件读取错误, 或读取操作通过 abort() 中止。 FileReader 不再读取 FileBlob。 如果 readyState 被设置为 DONE, 这意味着至少有一个 读取方法 已在此 FileReader 上被调用。

6.2.3. 读取文件或 Blob

FileReader 接口提供了几种 异步读取方法——readAsArrayBuffer(), readAsBinaryString(), readAsText()readAsDataURL(), 用于将文件读取到内存中。

注意: 如果在同一个 FileReader 对象上调用了多个并发的读取方法, 用户代理会在 InvalidStateError 中抛出异常, 当 readyState = LOADING 时。

(FileReaderSync 提供了几种 同步读取方法。 集体来说,FileReaderFileReaderSync 的同步和异步读取方法被统称为 读取方法。)

6.2.3.1. readAsDataURL() 方法

readAsDataURL(blob) 方法, 被调用时,必须为 blob 启动一个 DataURL读取操作

6.2.3.2. readAsText() 方法

readAsText(blob, encoding) 方法, 被调用时,必须为 blob 启动一个 Textencoding读取操作

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() 方法时, 用户代理必须执行以下步骤:

  1. 如果 this状态"empty"this状态"done",则将 thisresult 设置为 null终止此算法

  2. 如果 this状态"loading",则将 this状态 设置为 "done" 并将 thisresult 设置为 null

  3. 如果 任务this 中存在于 文件读取任务源 的相关 任务队列 中, 则将这些 任务 从该任务队列中移除。

  4. 终止 正在处理的 读取方法 的算法。

  5. 触发进度事件, 事件名为 abortthis

  6. 如果 this状态 不是 "loading"触发进度事件,事件名为 loadendthis

6.3. 打包数据

一个 Blob 具有一个关联的 打包数据 算法, 给定 bytestype,一个可选的 mimeType 和一个可选的 encodingName, 根据 type 切换并执行相关步骤:
DataURL

根据以下注意事项,将 bytes 作为 DataURL 返回 [RFC2397]

  • 如果可用,使用 mimeType 作为 Data URL 的一部分, 以符合 Data URL 规范 [RFC2397]

  • 如果 mimeType 不可用,则返回没有媒体类型的 Data URL。[RFC2397]

需要更好地指定如何生成 DataURL。[Issue #104]

Text
  1. encoding 设为失败。

  2. 如果存在 encodingName,则将 encoding 设置为 获取编码 的结果, 从 encodingName 获取。

  3. 如果 encoding 失败,并且 mimeType 存在:

    1. type 设置为 解析 MIME 类型 的结果, 给定 mimeType

    2. 如果 type 没有失败, 将 encoding 设置为从 type参数 ["charset"] 获取的 编码

      如果 blobtype 属性是 text/plain;charset=utf-8,那么将会使用 "utf-8" 标签运行 获取编码。 请注意,用户代理必须解析并提取字符集参数的部分,以构成编码的 标签
  4. 如果 encoding 失败,则将 encoding 设置为 UTF-8

  5. 使用备用编码 encoding 解码 bytes,并返回结果。

ArrayBuffer

返回一个新的 ArrayBuffer,其内容为 bytes

BinaryString

bytes 作为一个二进制字符串返回, 其中每个字节由一个值相等的代码单元 [0..255] 表示。

6.4. 事件

FileReader 对象必须是此规范中所有事件的事件目标。

当本规范要求 触发一个名为 e 的进度事件 时(对于某些 ProgressEvent e 在给定的 FileReader reader 上), 以下是规范要求:

6.4.1. 事件概述

以下是会在 触发FileReader 对象上的事件。

事件名称 接口 触发时机…
loadstart ProgressEvent 读取开始时。
progress ProgressEvent 正在读取(和解码) blob 时。
abort ProgressEvent 当读取已中止时。 例如,通过调用 abort() 方法。
error ProgressEvent 当读取失败时(请参阅文件读取错误)。
load ProgressEvent 读取成功完成时。
loadend ProgressEvent 请求已完成(无论成功与否)。

6.4.2. 事件不变性概述

本节为说明性内容。

以下是不变性,适用于在此规范中的给定异步 读取方法事件触发

  1. 一旦 loadstart 事件被触发, 对应的 loadend 会在读取完成时触发, 除非下列情况之一为真:

    注意: 事件 loadstartloadend 之间不是一对一的关系。

    这个例子展示了“读取链”:在事件处理程序中启动另一个读取,而“第一个”读取继续处理。
    // 如下代码...
    reader.readAsText(file);
    reader.onload = function(){reader.readAsText(alternateFile);}
    
    .....
    
    //... loadend 事件不应为第一次读取触发
    
    reader.readAsText(file);
    reader.abort();
    reader.onabort = function(){reader.readAsText(updatedFile);}
    
    //... loadend 事件不应为第一次读取触发
    
  2. blob 完全读取到内存中时,将触发一个 progress 事件。

  3. loadstart 事件之前,不会触发 progress 事件。

  4. abortloaderror 触发后,不会触发 progress 事件。 对于给定的读取,最多只触发一次 abortloaderror

  5. loadend 触发后,不会触发 abortloaderror 事件。

6.5. 在线程中读取

Web Workers 允许使用同步的 FileBlob 读取 API, 因为在线程上进行的读取不会阻塞主线程。 本节定义了一个可以在 Workers 内使用的同步 API [[Web Workers]]。 Workers 可以同时使用异步 API(FileReader 对象)同步 API(FileReaderSync 对象)。

6.5.1. FileReaderSync API

此接口提供了 同步读取 FileBlob 对象到内存中的方法。

[Exposed=(DedicatedWorker,SharedWorker)]
interface FileReaderSync {
  constructor();
  // Synchronously return strings

  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) 方法被调用时, 必须运行以下步骤:

  1. stream 设置为调用 get stream 方法获取的 blob 的结果。

  2. reader 设置为从 stream 中通过 获取 reader 方法得到的结果。

  3. promise 设置为通过 读取所有字节stream 中 使用 reader 得到的结果。

  4. 等待 promise 完成或被拒绝。

  5. 如果 promise 返回的结果为 字节序列 bytes

    1. 返回 package data 的结果,输入为 bytesTextblobtype,以及 encoding

  6. 抛出 promise 的拒绝原因。

6.5.1.3. readAsDataURL() 方法

readAsDataURL(blob) 方法, 被调用时,必须运行以下步骤:

  1. stream 设置为调用 get stream 方法获取的 blob 的结果。

  2. reader 设置为从 stream 中通过 获取 reader 方法得到的结果。

  3. promise 设置为通过 读取所有字节stream 中使用 reader 得到的结果。

  4. 等待 promise 完成或被拒绝。

  5. 如果 promise 返回的结果为 字节序列 bytes

    1. 返回 package data 的结果,输入为 bytesDataURL 以及 blobtype

  6. 抛出 promise 的拒绝原因。

6.5.1.4. readAsArrayBuffer() 方法

readAsArrayBuffer(blob) 方法, 被调用时,必须运行以下步骤:

  1. stream 设置为调用 get stream 方法获取的 blob 的结果。

  2. reader 设置为从 stream 中通过 获取 reader 方法得到的结果。

  3. promise 设置为通过 读取所有字节stream 中使用 reader 得到的结果。

  4. 等待 promise 完成或被拒绝。

  5. 如果 promise 返回的结果为 字节序列 bytes

    1. 返回 package data 的结果,输入为 bytesArrayBuffer 以及 blobtype

  6. 抛出 promise 的拒绝原因。

6.5.1.5. readAsBinaryString() 方法

readAsBinaryString(blob) 方法, 被调用时,必须运行以下步骤:

  1. stream 设置为调用 get stream 方法获取的 blob 的结果。

  2. reader 设置为从 stream 中通过 获取 reader 方法得到的结果。

  3. promise 设置为通过 读取所有字节stream 中使用 reader 得到的结果。

  4. 等待 promise 完成或被拒绝。

  5. 如果 promise 返回的结果为 字节序列 bytes

    1. 返回 package data 的结果,输入为 bytesBinaryString 以及 blobtype

  6. 抛出 promise 的拒绝原因。

注意: 使用 readAsArrayBuffer() 方法优先于 readAsBinaryString(), 后者是为了向后兼容而提供的。

7. 错误与异常

文件读取错误 可能会在从底层文件系统读取文件时发生。 下面列出的潜在错误情况是参考信息

7.1. 抛出异常或返回错误

本节为规范性内容。

在读取 文件Blob 时,可能会出现错误条件。

当读取 文件Blob 时,读取操作可能因错误条件而终止; 导致 get stream 算法失败的具体错误条件称为 失败原因失败原因 之一包括 NotFoundUnsafeFileTooManyReadsSnapshotStateFileLock

如果因某个特定 失败原因 出现错误,同步读取方法会 抛出下表中的异常。

异步读取方法使用 error 属性,返回 DOMException 对象(下表中的最合适类型), 如果出现错误是由于某个特定 失败原因, 否则返回 null。

类型 描述和失败原因
NotFoundError 如果在处理读取时,文件Blob 资源未找到, 这是 NotFound 失败原因

对于异步读取方法,error 属性必须返回 NotFoundError 异常, 同步读取方法必须 抛出 NotFoundError 异常。

SecurityError 如果:
  • 确定某些文件在 Web 应用程序中不安全访问, 这是 UnsafeFile 失败原因

  • 确定对 文件Blob 资源的读取调用次数过多, 这是 TooManyReads 失败原因

对于异步读取方法,error 属性可能会返回 SecurityError 异常, 同步读取方法可能会 抛出 SecurityError 异常。

这是在任何其他 失败原因 不适用的情况下使用的安全错误。

NotReadableError 如果:

对于异步读取方法,error 属性必须返回 NotReadableError 异常, 同步读取方法必须 抛出 NotReadableError 异常。

8. 用于 Blob 和 MediaSource 的 URL 参考

本节定义了用于引用 BlobMediaSource 对象的 URL 方案

8.1. 介绍

本节为说明性内容。

Blob(或对象)URL 是类似于 blob:http://example.com/550e8400-e29b-41d4-a716-446655440000 这样的 URL。 这使得 BlobMediaSource 能够与其他仅设计用于使用 URL 的 API 进行集成,例如 img 元素。Blob URL 也可用于导航或触发本地生成数据的下载。

为此目的,URL 接口公开了两个静态方法,createObjectURL(obj)revokeObjectURL(url)。 第一个方法创建从 URLBlob 的映射,第二个方法撤销该映射。 只要映射存在,Blob 就不能被垃圾回收, 因此在不再需要引用时,必须注意尽快撤销 URL。 当创建 URL 的全局对象消失时,所有 URL 都会被撤销。

8.2. 模型

每个用户代理都必须维护一个 blob URL 存储。 一个 blob URL 存储 是一个 映射,其中 有效的 URL 字符串blob URL 条目

一个 blob URL 条目包含一个对象(类型为 BlobMediaSource)和一个环境(一个环境设置对象)。

注意:规范必须使用获取 blob 对象算法来访问 blob URL 条目对象

blob URL 存储(也称为 blob URL)中是 有效的 URL 字符串,当 解析 后会生成一个 URL,其 方案等于 "blob", 空主机,以及一个由一个元素组成的 路径,该元素本身也是一个 有效的 URL 字符串

为了获取一个 blob 对象,给定一个 blob URL 条目 blobUrlEntry 和一个环境设置对象或字符串 "navigation" environment, 执行以下步骤。它们返回一个对象
  1. isAuthorized 为 true。

  2. 如果 environment 不是字符串 "navigation",则将 isAuthorized 设置为使用 blobUrlEntryenvironment 检查同分区 blob URL 使用情况的结果。

  3. 如果 isAuthorized 为 false,则返回失败。

  4. 返回 blobUrlEntry对象

生成一个新的 blob URL,请运行以下步骤:
  1. result 为空字符串。

  2. 将字符串 "blob:" 附加到 result

  3. settings当前设置对象

  4. originsettings来源

  5. serialized来源的 ASCII 序列化

  6. 如果 serialized 是 "null",则将其设置为实现定义的值。

  7. serialized 附加到 result

  8. 将 U+0024 SOLIDUS (/) 附加到 result

  9. 生成一个 UUID [RFC4122] 作为字符串并将其附加到 result

  10. 返回 result

由此算法生成的 blob URL 示例是 blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f64
为给定的 object 添加条目到 blob URL 存储,请运行以下步骤:
  1. store 为用户代理的 blob URL 存储

  2. url生成新的 blob URL 的结果。

  3. entry 为一个新的 blob URL 条目,包含 object当前设置对象

  4. 设置 store[url] 为 entry

  5. 返回 url

移除 blob URL 存储中的条目 为给定的 url,请运行以下步骤:
  1. store 为用户代理的 blob URL 存储

  2. url string序列化url 结果。

  3. 移除 store[url string]。

8.3. blob URL 的解析模型

为了解析 blob URL, 给定一个 URL url
  1. 断言url方案是“blob”。

  2. store 为用户代理的blob URL 存储

  3. url string 为设置了排除片段标志序列化 url 的结果。

  4. 如果 store[url string] 存在,则返回 store[url string];否则返回失败。

对于 blob URL 的解析和获取模型的进一步要求,在 [URL][Fetch] 规范中定义。

8.3.1. blob URL 的来源

本节为说明性内容。

blob URL 的来源总是与创建该 URL 的环境相同,只要该 URL 尚未被撤销。这是通过 [URL] 规范在解析 URL 时查找 blob URL 存储 中的条目,并使用该条目返回正确的来源来实现的。

如果 URL 被撤销,来源的序列化仍将与创建 blob URL 的环境的来源序列化相同,但对于不透明的来源,来源本身可能是不同的。然而,这种区别是不可观察到的,因为已撤销的 blob URL 无法再被解析或获取。

8.3.2. blob URL 的访问限制

Blob URL 只能从存储密钥与创建 blob URL 的环境的存储密钥匹配的环境中获取。Blob URL 导航不受此限制。

为了检查同分区 blob URL 使用情况,给定一个 blob URL 条目 blobUrlEntry 和一个环境设置对象 environment, 执行以下步骤。它们返回一个布尔值。
  1. blobStorageKey 为使用 blobUrlEntry环境为非存储目的获取存储密钥的结果。

  2. environmentStorageKey 为使用 environment 为非存储目的获取存储密钥的结果。

  3. 如果 blobStorageKeyenvironmentStorageKey相等,则返回 false。

  4. 返回 true。

8.3.3. blob URL 的生命周期

本规范通过以下步骤扩展了 卸载文档清理步骤

  1. environmentDocument相关设置对象

  2. store 为用户代理的 blob URL 存储

  3. store 中移除所有 环境 等于 environment 的条目。

当 worker 被卸载时也需要类似的钩子。

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) 静态方法必须返回obj 添加条目到 blob URL 存储的结果。
revokeObjectURL(url) 静态方法必须执行以下步骤:
  1. urlRecord解析 url 的结果。

  2. 如果 urlRecord方案不是“blob”,则返回。

  3. entryurlRecordblob URL 条目

  4. 如果 entry 为 null,则返回。

  5. isAuthorized 为使用 entry当前设置对象检查同分区 blob URL 使用情况的结果。

  6. 如果 isAuthorized 为 false,则返回。

  7. 从 Blob URL 存储中移除 url 的条目。

注意:这意味着尝试撤销未注册的 URL 或从不同存储分区的环境中注册的 URL 将会静默失败,而不是抛出某种错误。 如果发生这种情况,用户代理可能会在错误控制台中显示一条消息。

注意:在撤销 url 后尝试解引用它将导致 网络错误。 在撤销 url 之前启动的请求仍应成功。

在下面的示例中,window1window2 是独立的, 但在 同源window2 可能是 iframewindow1 内。
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 URL
url = URL.createObjectURL(blob);
img1.src = url;
img2.src = url;
在下面的示例中,显式调用了 URL.revokeObjectURL()
var blobURLref = URL.createObjectURL(file);
img1 = new Image();
img2 = new Image();

// 两个赋值操作均正常工作
img1.src = blobURLref;
img2.src = blobURLref;

// ... 页面加载后
// 检查两个图像是否已加载完成
if(img1.complete && img2.complete) {
  // 确保后续的引用抛出异常
  URL.revokeObjectURL(blobURLref);
} else {
  msg("无法预览图像!");
  // 撤销基于字符串的引用
  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 并未满足所有用例; 后续版本可能会解决这些问题。

致谢

本规范最初由 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 工作组,和 public-webapps@w3.org 邮件列表上的参与者。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语的组合进行表达。规范中的关键字 “MUST”、 “MUST NOT”、 “REQUIRED”、 “SHALL”、 “SHALL NOT”、 “SHOULD”、 “SHOULD NOT”、 “RECOMMENDED”、 “MAY”、和 “OPTIONAL” 应按照 RFC 2119 中的说明进行解释。然而,为了可读性,这些词在本规范中并未全部以大写字母出现。

本规范的所有文本都是规范性的,除非明确标记为非规范性、示例和注释的部分。[RFC2119]

本规范中的示例以 “例如” 这些词开头,或者与规范文本分开,并带有 class="example",如下所示:

这是一个说明性示例的例子。

说明性注释以“注意”开头,并与规范文本分开,带有 class="note",如下所示:

注意,这是一条说明性注释。

一致性算法

以命令形式出现的算法中的要求(例如 “删除任何前导空格字符” 或 “返回 false 并终止这些步骤”)应根据引入算法时使用的关键字(“必须”、“应”、“可以”等)进行解释。

以算法或特定步骤表述的一致性要求可以以任何方式实现,只要最终结果等效即可。特别是,本规范中定义的算法旨在易于理解,并不旨在追求性能。鼓励实现者进行优化。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL: https://dom.spec.whatwg.org/
[ECMA-262]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[ENCODING]
Anne van Kesteren. 编码标准. 现行标准. URL: https://encoding.spec.whatwg.org/
[Fetch]
Anne van Kesteren. Fetch 标准. 现行标准. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren 等. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[MEDIA-SOURCE-2]
Jean-Yves Avenard; Mark Watson. 媒体源扩展™。2024年7月4日。工作草案。URL:https://www.w3.org/TR/media-source-2/
[MIMESNIFF]
Gordon P. Hemsley. MIME 嗅探标准. 现行标准. URL: https://mimesniff.spec.whatwg.org/
[RFC2119]
S. Bradner. 在RFC中使用关键字以表明需求等级. 1997年3月. 最佳现行实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC2397]
L. Masinter. “data” URL 方案. 1998年8月. 提议标准. URL: https://www.rfc-editor.org/rfc/rfc2397
[RFC4122]
K. Davis; B. Peabody; P. Leach. 通用唯一标识符 (UUID). 2024年5月. 提议标准. URL: https://www.rfc-editor.org/rfc/rfc9562
[SERVICE-WORKERS]
Jake Archibald;Marijn Kruisselbrink。服务工作线程。2022年7月12日。CR。URL:https://www.w3.org/TR/service-workers/
[STORAGE]
Anne van Kesteren。存储标准。现行标准。URL:https://storage.spec.whatwg.org/
[STREAMS]
Adam Rice 等. 流标准. 现行标准. URL: https://streams.spec.whatwg.org/
[URL]
Anne van Kesteren. URL 标准. 现行标准. URL: https://url.spec.whatwg.org/
[WebIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行标准. URL: https://webidl.spec.whatwg.org/
[XHR]
Anne van Kesteren. XMLHttpRequest 标准. 现行标准. URL: https://xhr.spec.whatwg.org/

参考性引用

[Workers]
Ian Hickson. Web Workers. 2021年1月28日. 注释. URL: https://www.w3.org/TR/workers/

IDL 索引

[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 chunks
  Blob slice(optional [Clamp] long long start,
            optional [Clamp] long long end,
            optional DOMString contentType);

  // read from the Blob.
  [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;

[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;
};

[Exposed=(Window,Worker), Serializable]
interface FileList {
  getter File? item(unsigned long index);
  readonly attribute unsigned long length;
};

[Exposed=(Window,Worker)]
interface FileReader: EventTarget {
  constructor();
  // async read methods
  undefined readAsArrayBuffer(Blob blob);
  undefined readAsBinaryString(Blob blob);
  undefined readAsText(Blob blob, optional DOMString encoding);
  undefined readAsDataURL(Blob blob);

  undefined abort();

  // states
  const unsigned short EMPTY = 0;
  const unsigned short LOADING = 1;
  const unsigned short DONE = 2;

  readonly attribute unsigned short readyState;

  // File or Blob data
  readonly attribute (DOMString or ArrayBuffer)? result;

  readonly attribute DOMException? error;

  // event handler content attributes
  attribute EventHandler onloadstart;
  attribute EventHandler onprogress;
  attribute EventHandler onload;
  attribute EventHandler onabort;
  attribute EventHandler onerror;
  attribute EventHandler onloadend;
};

[Exposed=(DedicatedWorker,SharedWorker)]
interface FileReaderSync {
  constructor();
  // Synchronously return strings

  ArrayBuffer readAsArrayBuffer(Blob blob);
  DOMString readAsBinaryString(Blob blob);
  DOMString readAsText(Blob blob, optional DOMString encoding);
  DOMString readAsDataURL(Blob blob);
};

[Exposed=(Window,DedicatedWorker,SharedWorker)]
partial interface URL {
  static DOMString createObjectURL((Blob or MediaSource) obj);
  static undefined revokeObjectURL(DOMString url);
};

问题索引

我们需要更具体地说明从 Blob 读取内容的实际操作,以及可能发生的错误,或许还包括一些关于数据块大小的内容等。
我们可能会更改 loadstart 为同步调度, 以与 XMLHttpRequest 行为保持一致。[Issue #119]
更好地规范如何生成 DataURL。[Issue #104]
当 worker 被卸载时需要一个类似的钩子。
本节为临时性内容;后续版本可能会补充更多的安全数据。
MDN

Blob/Blob

In all current engines.

Firefox13+Safari6+Chrome20+
Opera12+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12+
Node.js15.7.0+
MDN

Blob/arrayBuffer

In all current engines.

Firefox69+Safari14+Chrome76+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js15.7.0+
MDN

Blob/size

In all current engines.

Firefox4+Safari6+Chrome5+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile11+
Node.js15.7.0+
MDN

Blob/slice

In all current engines.

Firefox13+Safari7+Chrome21+
Opera12+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12+
Node.js15.7.0+
MDN

Blob/stream

In all current engines.

Firefox69+Safari14.1+Chrome76+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js15.7.0+
MDN

Blob/text

In all current engines.

Firefox69+Safari14+Chrome76+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js15.7.0+
MDN

Blob/type

In all current engines.

Firefox4+Safari6+Chrome5+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile11+
Node.js15.7.0+

File/type

In all current engines.

Firefox3.6+Safari8+Chrome13+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile14+
MDN

Blob

In all current engines.

Firefox4+Safari6+Chrome5+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile11+
Node.js18.0.0+
MDN

File/File

In all current engines.

Firefox28+Safari10.1+Chrome38+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

File/lastModified

In all current engines.

Firefox15+Safari10+Chrome13+
Opera15+Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile14+
MDN

File/name

In all current engines.

Firefox3.6+Safari8+Chrome13+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile14+
MDN

File

In all current engines.

Firefox7+Safari4+Chrome13+
Opera11.5+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile11.5+
MDN

FileList/item

In all current engines.

Firefox3+Safari4+Chrome2+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile12.1+
MDN

FileList/length

In all current engines.

Firefox3+Safari4+Chrome2+
Opera11.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile11.1+
MDN

FileList

In all current engines.

Firefox3+Safari4+Chrome2+
Opera11.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile11.1+
MDN

FileReader/FileReader

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile12.1+
MDN

FileReader/abort

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/abort_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/abort_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/error

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/error_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/error_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/load_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/load_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/loadend_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/loadend_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/loadstart_event

In all current engines.

Firefox79+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android79+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/loadstart_event

In all current engines.

Firefox79+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android79+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/progress_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/progress_event

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/readAsArrayBuffer

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera12+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile12+
MDN

FileReader/readAsBinaryString

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IENone
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/readAsDataURL

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/readAsText

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/readyState

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader/result

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReader

In all current engines.

Firefox3.6+Safari6+Chrome6+
Opera11+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android32+iOS Safari?Chrome for Android?Android WebView3+Samsung Internet?Opera Mobile11+
MDN

FileReaderSync/FileReaderSync

In all current engines.

Firefox8+Safari6+Chrome7+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
MDN

FileReaderSync/readAsArrayBuffer

In all current engines.

Firefox8+Safari6+Chrome9+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
MDN

FileReaderSync/readAsDataURL

In all current engines.

Firefox8+Safari6+Chrome7+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
MDN

FileReaderSync/readAsText

In all current engines.

Firefox8+Safari6+Chrome7+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
MDN

FileReaderSync

In all current engines.

Firefox8+Safari6+Chrome7+
Opera12.1+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile12.1+
MDN

URL/createObjectURL_static

In all current engines.

Firefox19+Safari6+Chrome19+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js16.7.0+
MDN

URL/revokeObjectURL_static

In all current engines.

Firefox19+Safari6+Chrome19+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
Node.js16.7.0+