Warning:
This wiki has been archived and is now read-only.

File

From HTML5 Chinese Interest Group Wiki
Jump to: navigation, search

介绍

符合规范

依赖关系

术语及算法

FileList接口

该接口是一个File对象的集合#DOMCore

interface FileList {
    getter File? item(unsigned long index);
    readonly attribute unsigned long length;
};

这是典型的通过DOM访问表单中file元素选中的文件的例子。

// uploadData is a form element
// fileChooser is input element of type 'file'
var file = document.forms['uploadData']['fileChooser'].files[0];

if(file)
{
  // Perform file ops
}

特性

length
必须返回集合FileList中文件的个数。如果集合中没有任何文件,则必须返回0

方法

item(index)
必须返回集合FileList中的第indexFile对象。如果集合中不存在第indexFile对象,则必须返回null。
index必须被用户代理作为FileList集合中的File对象的位置来看待,且这个位置的值是从0开始计算的。支持的位置范围从0开始,且比FileListFile对象总数小。如果该文件不存在,则它也没有相应的位置#WebIDL
注:HTMLInputElement #HTML有一个FileList类型的只读特性,其访问方式见上面的例子。其它FileList类型的只读特性定义在了DataTransfer接口中#HTML

Blob接口

这个接口用来处理原始数据。它提供一种方法对数据对象在特定的字节范围内进行原始数据块的截取,同时它页提供一个特性用来显示数据块的大小。File接口也继承于此。

interface Blob {
     
    readonly attribute unsigned long long size;
    readonly attribute DOMString type;
    
    //slice Blob into byte-ranged chunks
    
    Blob slice(optional long long start,
            optional long long end,
            optional DOMString contentType); 
    
};

特性

size
展示Blob对象的字节数
type
代表文件媒体类型的小写ASCII编码字串,以RFC2046 MIME类型的方式表现 #RFC2046。如果文件的MIME类型是已知的,则用户代理应该将其返回。如果实现中无法判定文件的MIME类型,则必须返回空字符串。如果一个字符串能够匹配RFC 2616 #HTTP的第3.7章节“Media Types”中的media-type令牌,则它是一个有效的MIME类型。
注:取消引用blob地址时,使用type特性进行编码判断并解析Content-Type头

方法和参数

slice方法
根据规定的字节范围返回一个新的Blob对象。
start参数值是调用slice时的起点。
length参数值是调用slice时从起点到终点的差值。
slice方法必须将超出size范围的索引计算调整到其范围以内。也就是说,对于一个slice调用:
  • 如果start + length < size那么用户代理必须返回相当于slice(start, size-start)结果的Blob对象。
  • 如果start > size那么用户代理必须返回一个长度为0的Blob对象。
注:另一个方案是抛出带有INDEX_SIZE_ERR标记的DOMException

File接口

这个接口继承自Blob,描述了在FileList中的单个文件。

interface File : Blob {

    readonly attribute DOMString name;
    readonly attribute Date lastModifiedDate;
}

特性

name
文件名称。在不同的系统内有多种文件名称变化,这里只是文件的名称,并不附加路径信息。
lastModifiedDate
文件的上次修改日期。取值时,必须返回Date格式的上次修改时间,如果无法得到有效的信息,则返回null

FileReader接口

此接口为File对象或Blob对象读取到内容中提供方法,并且继承EventTarget,通过过程事件和事件句柄特性访问数据。我们推荐在主线程中异步读取文件系统中的数据。该接口提供了一套异步API,在全局对象中使用。

[Constructor]
interface FileReader: EventTarget {

    // async read methods
    void readAsArrayBuffer(Blob blob);
    void readAsBinaryString(Blob blob);
    void readAsText(Blob blob, optional DOMString encoding);
    void readAsDataURL(Blob blob);

    void 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 any result;

    readonly attribute DOMError error;

    // event handler attributes
    attribute [TreatNonCallableAsNull] Function? onloadstart;
    attribute [TreatNonCallableAsNull] Function? onprogress;
    attribute [TreatNonCallableAsNull] Function? onload;
    attribute [TreatNonCallableAsNull] Function? onabort;
    attribute [TreatNonCallableAsNull] Function? onerror;
    attribute [TreatNonCallableAsNull] Function? onloadend;

};

FileReader的任务来源

我们可以利用FileReader接口,通过触发过程事件,异步读取单个Blob对象,这些事件会以EventTarget绑定为FileReader的事件句柄方法#DOMCore。除非另有说明,该规范中的任务来源都是FileReader。此任务来源用来异步依次触发事件,当读取方法触发完毕之后,其result特性会被更新。

构造函数

当构造函数FileReader()被调用时,用户代理必须返回一个新的FileReader对象。 不论全局对象代表Window还是代表WorderGlobalScopeFileReader都必须可用。

事件句柄特性

下表FileReader在DOM特性中必须支持的事件句柄特性(以及它们相应的事件类型):

事件句柄特性 事件句柄事件类型
onloadstart loadstart
onprogress progress
onabort abort
onerror error
onload load
onloadend loadend

FileReader的状态

FileReader对象处于以下三种状态之一。其readyState特性在被访问时必须返回当前状态所对应的值:

EMPTY (值为0)
FileReader对象已经建立,但没有执行任何读取操作。没有读取方法被调用。这正式一个FileReader对象创建之初的默认状态,该状态会持续到我们调用其读取方法为止。
LOADING (值为1)
一个FileBlob正在读取中。一个读取方法正在执行且在读取过程中没有错误发生。
DONE (值为2)
整个FileBlob完全读进内存中,或一个文件错误在读取过程中被触发,或读取过程被abort()函数中断。FileReader不再读取FileBlob。如果readyState被设置为DONE那么至少有一个读取方法被FileReader调用过了。

读取一个File或Blob

多种读取方式

FileReader接口提供了四种异步读取方法:readAsArrayBufferreadAsBinaryStringreadAsTextreadAsDataURL,这些方法会将文件读取到内存中。如果同一个FileReader对象同时并发多个读取方法,则用户代理必须在每个读取方法执行时,待readyState的值为LOADING后,抛出InvalidStateError异常#DOMCore

result特性

我们在访问result特性时,会得到一个Blob的数据,其格式或为DOMString、或为ArrayBuffer#TypedArrays或为null,这取决于FileReader对象调用的读取方法以及可能出现的各种错误。访问该特性还可以得到Blob数据片段,即内存中的FileBlob数据的一部分。当读取操作readAsBinaryStringreadAsText正在进行中时,这个Blob数据片段是一个DOMString;当读取操作readAsArrayBuffer正在进行中时,这个Blob数据片段就是一个ArrayBuffer对象#TypedArrays。(more bytes loaded (a portion of the total))#ProgressEvents。下列列表是result特性的规范性一致性标准:

  • 取值时,如果readyState的值是EMPTY(没有调用任何读取方法),则result特性必须返回null
  • 取值时,如果读取FileBlob出错,则result特性必须返回null
  • 取值时,如果使用的方法是readAsDataURL,则result特性必须返回一个DOMString,其值是这个FileBlob的数据的DataURL编码#DataURL
  • 取值时,如果使用的方法是readAsBinaryString并且在读取过程中没有出错,则result特性必须返回一个代表这个FileBlob的二进制字符串的DOMString。该二进制字符串即每个字节代表一个整形范围。在读取过程中取值时,result特性也应该以二进制字符串的DOMString不断返回Blob数据片段。用户代理必须返回至少一个这样的result,最后在读取完成之后返回一个完整的result
  • 取值时,如果使用的方法是readAsText并且在读取过程中没有出错,则result特性必须返回一个代表这个FileBlob的文本的DOMString。该文本需要以特定的解码方式在内存中解码。在读取过程中取值时,result特性也应该以文本的DOMString不断返回Blob数据片段。用户代理必须返回至少一个这样的result,最后在读取完成之后返回一个完整的result。详见以给定的编码方式获取Blob数据片段的备忘。
  • 取值时,如果使用的方法是readAsArrayBuffer并且在读取过程中没有出错,则result特性必须返回一个ArrayBuffer#TypedArrays对象。在读取过程中取值时,result特性应该返回一个ArrayBuffer#TypedArrays形式的Blob数据片段。用户代理必须返回至少一个这样的result,最后在读取完成之后返回一个完整的result
注:如果读取操作成功,result特性必须返回一个非空(non-null)值,仅在过程事件#ProgressEvents触发之后,所有的读取访问Blob数据都是异步的。更新resultBlob数据的任务都是排队进行的。

readAsBinaryString(blob)方法

readAsBinaryString(blob)方法被调用时,用户代理必须依次执行下面的步骤(除非另有说明)。

  1. 如果readyState的值是LOADING,则抛出一个InvalidStateError异常#DOMCore并终止运行。注意readAsBinaryString()方法会由于此算法终止运行而立刻返回。
  2. 如果读取参数blob时出错,则将readyState的值设为DONE同时把result设为null。然后运行下面的错误处理。
    1. 触发一个名为error的过程事件。设置error特性的读取操作必须是一个DOMError对象,这个对象可以表明文件出错的类型。
    2. 触发一个名为loadend的过程事件。
    3. 终止运行。注意readAsBinaryString()方法会由于此算法终止运行而立刻返回。
  3. 如果没有错误出现,则设置readyState的值为LOADING
  4. 触发一个名为loadstart的过程事件。
  5. 返回readAsBinaryString()方法,但继续执行下面的算法步骤。
  6. 生成进度提醒。
  7. 在读取时,一旦从blob而来的数据成为可用数据,用户代理应该在读取完成之前,不断伴随触发progress事件,把Blob数据片段更新到result中,并且这些任务是被排队触发执行的。在向result特性取值时,得到的是现已载入字节数的Blob数据片段(作为总数的其中一部分)#ProgressEvents,用户代理必须以二进制字符串的格式,返回至少一个这样的result。最后一次返回的值即为完整的读取结果。
  8. blob全部读入内存之后,将readyState的值设为DONE
  9. 终止运行。
注:我们不推荐使用readAsBinaryString()方法,并推荐用户代理支持readAsArrayBuffer(blob)方法替换掉readAsBinaryString()

readAsDataURL(blob)方法

readAsDataURL(blob)方法被调用时,用户代理必须依次执行下面的步骤(除非另有说明)。

  1. 如果readyState的值是LOADING,则抛出一个InvalidStateError异常#DOMCore并终止运行。注意readAsDataURL()方法会由于此算法终止运行而立刻返回。
  2. 如果读取参数blob时出错,或如果用户代理的URL长度限制阻止了Data URL#DataURL数据的返回,则将readyState的值设为DONE同时把result设为null。然后运行下面的错误处理。
    1. 触发一个名为error的过程事件。设置error特性的读取操作必须是一个DOMError对象,这个对象可以表明文件出错的类型。
    2. 触发一个名为loadend的过程事件。
    3. 终止运行。注意readAsDataURL()方法会由于此算法终止运行而立刻返回。
  3. 如果没有错误出现,则设置readyState的值为LOADING
  4. 触发一个名为loadstart的过程事件。
  5. 返回readAsDataURL()方法,但继续执行下面的算法步骤。
  6. 生成进度提醒。
  7. 将一个任务排队,这个任务待其blob被完全读入内存之后,以DataURL#DataURL格式更新到result特性中。当取值时,result特性返回完整的blob的Data URL#DataURL数据。
    1. 如果blobtype特性可用,那么它会根据DataURL的规范作为DataURL的一部分#DataURL
    2. 如果blobtype特性不可用,则返回不包含媒体类型的DataURL#DataURL注意:无媒体类型#RFC2046的DataURL#DataURL会被作为文本类型对待。
  8. readyState的值设为DONE
  9. 触发一个名为load的过程事件。
  10. 触发一个名为loadend的过程事件。
  11. 终止运行。

readAsText(blob, encoding)方法

readAsText(blob, encoding)方法被调用时(encoding参数是可选的),用户代理必须依次执行下面的步骤(除非另有说明)。

  1. 如果readyState的值是LOADING,则抛出一个InvalidStateError异常#DOMCore并终止运行。注意readAsText()方法会由于此算法终止运行而立刻返回。
  2. 如果读取参数blob时出错,则将readyState的值设为DONE同时把result设为null。然后运行下面的错误处理。
    1. 触发一个名为error的过程事件。设置error特性的读取操作必须是一个DOMError对象,这个对象可以表明文件出错的类型。
    2. 触发一个名为loadend的过程事件。
    3. 终止运行。注意readAsText()方法会由于此算法终止运行而立刻返回。
  3. 如果没有错误出现,则设置readyState的值为LOADING
  4. 触发一个名为loadstart的过程事件。
  5. 返回readAsText()方法,但继续执行下面的算法步骤。
  6. 生成进度提醒。
  7. 在读取时,一旦从blob而来的数据成为可用数据,用户代理应该在读取完成之前,不断伴随触发progress事件,把Blob数据片段更新到result中,并且这些任务是被排队触发执行的。在向result特性取值时,得到的是现已载入字节数的Blob数据片段(作为总数的其中一部分)#ProgressEvents,用户代理必须以指定编码格式的文本,返回至少一个这样的result。最后一次返回的值即为完整的读取结果。
  8. blob全部读入内存之后,将readyState的值设为DONE
  9. 终止运行。
注:Blob数据片段必须返回当前编码方式下任何可能的合法代码,尤其是判断Blob数据片段的编码方式时,用户代理禁止为不合法字节返回U+FFFD字符,直到整个代码点被读取。比如:假设一个文件资源,以UTF-8方式读取,在16进制下,文件的字节内容为E3 83 91 E3 83 91,即0x30D1 0x30D1。假设前5个字节已经被读取。result返回的必须是0x30D1且长度为1,而不是0x30D1 0xFFFD且长度为2。即使结尾的第五字节E3 83并不是合法代码点在UTF-8下,用户代理禁止返回一个代表错误代码点的U+FFFE作为result的返回值。

readAsArrayBuffer(blob)方法

readAsArrayBuffer(blob)方法被调用时,用户代理必须依次执行下面的步骤(除非另有说明)。

  1. 如果readyState的值是LOADING,则抛出一个InvalidStateError异常#DOMCore并终止运行。注意readAsArrayBuffer()方法会由于此算法终止运行而立刻返回。
  2. 如果读取参数blob时出错,则将readyState的值设为DONE同时把result设为null。然后运行下面的错误处理。
    1. 触发一个名为error的过程事件。设置error特性的读取操作必须是一个DOMError对象,这个对象可以表明文件出错的类型。
    2. 触发一个名为loadend的过程事件。
    3. 终止运行。注意readAsArrayBuffer()方法会由于此算法终止运行而立刻返回。
  3. 如果没有错误出现,则设置readyState的值为LOADING
  4. 触发一个名为loadstart的过程事件。
  5. 返回readAsArrayBuffer()方法,但继续执行下面的算法步骤。
  6. 生成进度提醒。
  7. 在读取时,一旦从blob而来的数据成为可用数据,用户代理应该在读取完成之前,不断伴随触发progress事件,把Blob数据片段更新到result中,并且这些任务是被排队触发执行的。在向result特性取值时,得到的是现已载入字节数的Blob数据片段(作为总数的其中一部分)#ProgressEvents,用户代理必须以ArrayBuffer 格式#TypedArrays的文本,返回至少一个这样的result。最后一次返回的值即为完整的读取结果。
  8. blob全部读入内存之后,将readyState的值设为DONE
  9. 终止运行。

abort()方法

abort()方法被调用时,用户代理必须依次执行下面的步骤:

  1. 如果readyState的值是EMPTYDONE,则设置result特性为null并终止运行剩余的步骤,不再做任何处理。
  2. 如果readyState的值是LOADING则设置readyState的值为DONE,同时设置resultnull
  3. 移除任务队列中由此FileReader对象引发的所有任务。
  4. 终止所有正在进行的读取方法。
  5. 触发一个名为abort的过程事件
  6. 触发一个名为loadend的过程事件
  7. 终止运行

Blob参数

本规范中有多个方法都强制要求传入Blob参数。

blob
这是一个FileReader的四个异步读取方法以及FileReaderSync的四个同步读取方法都会所调用的Blob参数。该参数必须是一个FileList中的单个File,或者是没有从文件系统获取,而是在当前方法下创建的Blob对象。

编码类型判定

当使用readAsText()方法读取blob对象时,可选参数encoding必须是一个字符集的名称或别名#IANACHARSET,否则均视为无效。下面是编码类型判定必须遵循的步骤:

  1. 按照encoding参数表示的类型对blob进行解码,如果该参数已提供且有效,则不必进行接下来的其它步骤。如果该参数无效,或未提供,或用户代理无法判定编码类型,则进入下一步。
  2. 使用参数blobtype特性作为字符参数#RFC2046进行解码,如果该type特性不存在,或者其提供的并不是可用的互联网字符集,则进入下一步。
  3. 使得字符集为null
  4. 从第一行开始,跟下面的表格中的每一行往后匹配,如果blob的开头的几个字节匹配第一列列出的内容,则使用其对应的编码类型。如果仍未匹配,则字符集仍未null。
Bytes in Hexadecimal Description
FE FF UTF-16BE BOM
FF FE UTF-16LE BOM
EF BB BF UTF-8 BOM
  1. 如果字符集仍未null则设字符集为UTF-8
  2. 返回按最终字符集的类型解码blob。对FileReader对象的result特性取值时、用FileReaderSync对象调用readAsText方法时,都返回该字符集格式的字符串。将使用当前字符集下无效的字节或字节序列替换为单个U+FFFD字符#Unicode。当处理Blob数据片段的时候,如果可用的话,使用encoding caveat。

事件

本规范提及的读取方法的运行进度提醒,均必须遵循下列步骤:

  1. 当读取操作进行时,在FileReader对象上排队执行触发一个名为progress的过程事件,事件每50ms或每字节触发一次,以较少者为准。在load事件出发之前至少要触发一个progress事件,即在100%完成读取操作时。如果100%blob在50ms之内就被读取进了内存,用户代理必须触发一个名为progress的过程事件以示完成。
  2. blob数据完全读取进入内存后,在FileReader对象上排队执行触发一个名为load的事件。
  3. blob数据完全读取进入内存后,在FileReader对象上排队执行触发一个名为loadend的事件。
如果一个已知实现的缓冲空间为65536字节,并以此限制读取操作,那么读取一个65537字节的文件时,必须这样触发事件:读取第1个65536字节时先触发一个progress事件,然后在读取第2次时(其实这次只读取了1个字节,文件已经结束),再触发一个progress事件,最后触发一个load事件和一个loadend事件。

本规范提及的“(为reader)触发一个名为e的过程事件”,均遵循下列步骤:

  • 过程事件e并不冒泡,e.bubbles的值必须为false #DOMCore
  • 过程事件e并不能被取消,e.cancelable的值必须为false #DOMCore
  • 术语“触发一个事件”定义于DOM Core #DOMCore。过程事件定义于Progress Events #ProgressEvents
事件概述

以下是在FileReader对象上触发的事件。触发事件定义于DOM Core #DOMCore,以下表格里由本规范定义的事件相关內容具有规范性。

事件名称 接口 出发时机
loadstart ProgressEvent 当读取开始时
progress ProgressEvent 当读取blob同时通报Blob数据片段时
abort ProgressEvent 当读取操作中断时,比如通过调用abort()方法中断读取操作
error ProgressEvent 当读物操作失败时
load ProgressEvent 当读取操作完成时
loadend ProgressEvent 当读取请求完成时(不论成功还是失败)
事件之间的规律

以下是本规范中异步读取方法触发的事件的规范性规律。

  1. 一旦loadstart已经被触发,则一个相应的loadend会在读取操作完成时触发,除非读取方法被abort()取消同时一个新的读取方法已经被调用。
  2. blob完成读入内存后,一个progress事件将被触发。
  3. loadstart事件之前没有progress事件。
  4. abortloadloadend事件之后没有progress事件。
  5. loadend事件之后没有abort事件、load事件、error事件。
注:loadstart事件和loadend事件并不是以一对一的方式耦合的。

在线程上进行读操作

在Web Workers生成的线程上进行读操作并不会阻塞主线程,因此用户可以对FileBlob接口的读取API进行同步调用。本章节定义可在Workers[Web Workers]中使用的同步API。Workers可以同时利用异步API(FileReader对象)同步API(FileReaderSync对象)。

FileReaderSync接口

该接口提供对将FileBlob对象'同步地读入'内存的方法。

[Constructor]
interface FileReaderSync {

  // Synchronously return strings

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

构造器

当调用FileReaderSync()构造器时,用户代理必须返回一个新建的FileReaderSync对象。

如果当前环境的全局对象是一个WorkerGlobalScope对象,则FileReaderSync构造器必须可用。

readAsText方法

当调用readAsText(blob, encoding)方法时(encoding参数可选)时,必须执行以下步骤:

  1. 如果读取blob参数的过程中发生错误,则抛出合适的异常,并终止所有步骤。
  2. 如果未发生任何错误,则将blob读入内存,使用编码检测算法获取blob的数据内容并返回。

readAsDataURL方法

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

  1. 如果读取blob参数的过程中发生错误,则抛出合适的异常,并终止所有步骤。
  2. 如果未发生任何错误,则将blob读入内存,并以Data URL[DataURL]的形式返回blob的数据内容:
    • 如果blob拥有type属性,则将其值作为Data URL的一部分,以符合Data URL[DataURL]规范的要求。
    • 如果blob没有type属性,则返回一个不含媒体类型的Data URL[DataURL]。
      对于符合规范的用户代理,必须将不含媒体类型[[http://dev.w3.org/2006/webapi/FileAPI/#RFC2046 RFC2046]]的Data URL作为纯文本处理。[[http://dev.w3.org/2006/webapi/FileAPI/#DataURL DataURL]]。

readAsArrayBuffer方法

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

  1. 如果读取blob参数的过程中发生错误,则抛出合适的异常,并终止所有步骤。
  2. 如果未发生任何错误,则将blob读入内存,并以ArrayBuffer[强类型数组]的形式返回blob的数据内容。

错误及异常

在从底层文件系统读取文件时,可能出现错误状态。以下列出了可能发生错误的情况,但不具规范意义

  • 当某次调用异步的读取方法或同步的读取方法时,访问的FileBlob并不存在。这种情况的出现可能是因为针对资源的引用创建之后,资源被移动或删除了(例如其他应用并发地修改了资源)。此时参见NotFoundError章节。
  • FileBlob不可读。这种情况的出现可能是因为针对资源的引用创建之后,出现了权限问题(例如其他应用对资源加了并发锁)。此时参见NotReadableError章节。
  • 在Web应用中,用户代理可能将某些文件鉴定为不安全的。这可能是在用户选择了文件之后,文件又在磁盘上被修改了,导致不合法的读取。另外有些文件或文件夹可能被底层文件系统加以限制,试图读取此类资源会被认为违反了安全策略。关于安全问题详见安全考量章节。此类问题出现时参见SecurityError章节。
  • 对Web应用的数据结构而言,某些文件体积可能过大。例如用户代理针对Data URL有URL的长度限制,此时可能导致无法以Data URL编码的形式返回大文件[DataURL]。此时参见EncodingError章节。

抛出异常或返回错误

本章节不具规范意义。

当读取文件时,可能出现错误状态。

当读取文件发生错误时,同步的读方法需要按下表的描述抛出相应的异常,否则必须从下表中选择一个最合适的DOMError对象[DOMCore],且FileReadererror属性必须返回该对象,对于其他情况则返回null。

错误类型 描述
NotFoundError 当进行读操作时,无法找到FileBlob资源,则如果使用的是异步读方法,error属性必须返回一个"NotFoundError"DOMError对象;而对于同步读方法,则必须抛出一个NotFoundError异常。
SecurityError 如果:
  • 确定在Web应用中访问特定文件是不安全的。
  • 确定在FileBlob对象上进行了过多的读操作。
  • 确定在用户选择文件后该文件被修改了。

则如果使用的是异步读方法,error属性可以返回一个"SecurityError"DOMError对象;而对于同步方法,可以抛出一个SecurityError异常。 当其他类型的异常都无法覆盖当前情况时,使用安全异常。

NotReadableError 如果FileBlob不可读,则如果使用的是异步读方法,error属性必须返回一个"NotReadableError"DOMError对象;而对于同步读方法,则必须抛出一个NotReadableError异常。这通常是因为针对资源的引用创建之后,出现了权限问题(例如其他应用对资源加了并发锁)。
EncodingError 如果针对Data URL的URL长度限制导致FileBlob无法以Data URL[DataURL]形式表示,则如果使用的是异步读方法,error属性必须返回一个"EncodingError"DOMError对象;而对于同步方法,必须抛出一个EncodingError异常。用户代理不得在调用异步及同步的readAsText()方法时使用该异常,原因是调用该方法时,编码由编码检测算法决定。

Blob及File的URI参考

本章节定义一个URI大纲,用于指向Blob(及File)对象。

新大纲的需求

本章节定义了blob:550e8400-e29b-41d4-a716-446655440000#aboutABBA类的URI大纲,并提供一些需求,本章节仅作为非正式的讨论使用。

  • 该大纲应当可以被Web API如XMLHttpRequest[XHR2]使用,同时其被设计为和HTTP URI一起可被元素使用,如img元素[HTML]。概括而言,根据该大纲的设计,在一切可以使用URI的场景,都应该可以使用该大纲。
    该大纲应当定义相应的响应码,以便Web应用可以在资源未找到或者发生错误时作出响应。
  • 该大纲应当有一个策略以及一个生命周期的规定,以便于从Web应用安全地访问二进制数据。
    该大纲下的URI应当用于引用“内存中的”Blob,同时也可以在平台的其它场景下得以重用,来指定二进制资源(例如视频会议[流API])。因为通常用于访问“内存中的”资源,因此该大纲下的URI被设计为非永久性的。
  • 开发者应当可以撤销该大纲下的URI,撤销后URI将不再代表Blob对象。此类情况包括程序不再需要所引用的文件,以及其他使用Blob对象的场景。考虑这样一个场景,通过canvas元素及其API[HTML]绘图,并导出一个Blob对象。此时通过导出一个Blob对象,可以创建当前绘画的截图,并使用该大纲通过<img>[HTML]元素来展示截图,当用户删除截图时,在内存中所有通过URI对该截图的引用都应当失效,因此需要URI可以撤销。

针对现有大纲的讨论

本章节对现有的大纲进行非正式的讨论,针对在上文的用例,这些大纲可能会被重新提议或重用,同时本章节还会就为何一个全新的大纲更为合适提出理由。待讨论的大纲包括HTTP[HTTP]、文件[RFC1630][RFC1738]及类似urn:uuid[RFC4122]的大纲。针对大纲选择的最广泛的考虑是,此大纲是否对Web开发者有直观的吸引力。

Blob URI大纲的定义

本章节使用正式的语法定义blob:URI大纲。一个blob:URI由blob:大纲以及一个不透明字符串组成,随后跟随一个可选的片断标识符。在本规范中不透明字符串是通过启发式方法生成的具有唯一性的字符串,其要求是2个串相似的机率很小,并且很难通过猜测得到。(例如由[RFC4122]定义的全球唯一标识符(UUID)可用作不透明字符串]])。一个片段标识符是可选的,当使用时,根据BlobFile资源的媒体类型,有不同的释义,参考[RFC2046]。

本章节使用[RFC5234]定义的扩充巴科斯 - 诺尔范式(ABNF)。所有blob:URL必须符合如下ABNF:

blob = scheme ":" opaqueString [fragIdentifier]

scheme = "blob"

; scheme is always "blob"

; opaqueString tokens MUST be globally unique
; opaqueString could be a UUID in its canonical form

不透明字符串

不透明字符串不得包含任何未经百分号编码[RFC3986]规定的保留字符,这些字符必须通过百分号编码。不透明字符串必须是全球唯一的。此类字符串应当只使用在U+002A至U+002B、U+002D至U+002E、U+0030至U+0039、U+0041至U+005A, U+005E至U+007E[Unicode]这些范围内的字符,并且应当至少由36个字符组成。UUID是一个潜在的用作[Blob URI]的不透明字符串的可选项,同时强烈推荐使用UUID。UUID由[RFC4122]定义,UUID的ABNF可参见附录A

关于片段标识符的讨论

需要潜在地获取表现,,一个片段的格式、解析以及处理指令依赖于其媒体类型[RFC2046],虽然该获取过程仅当blob: URI提取后才会进行。例如在一个HTML文件[HTML]中,片段标识符可用于指向文件中的一个锚点。如果用户代理无法识别资源的媒体类型,或片段标识符在该资源中没有任何意义,则必须忽略该片段标识符。此外,用户代理必须根据相应的媒体格式的规范,接收额外的片段处理指令。尤其是这包含了在HTML[HTML]中对片段产生式的任何修改。以下章节大致上是片段标签符的正规ABNF,但是需要注意的是相关的规范可能会对此定义进行扩充:

 fragIdentifier = "#" fragment

; Fragment Identifiers depend on the media type of the Blob
; fragment is defined in [RFC3986]
; fragment processing for HTML is defined in [HTML]

fragment    = *( pchar / "/" / "?" )

pchar       = unreserved / pct-encoded / sub-delims / ":" / "@"

unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"

pct-encoded   = "%" HEXDIG HEXDIG

sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
                 / "*" / "+" / "," / ";" / "="

一个合法的Blob URI引用可以像这样:blob:550e8400-e29b-41d4-a716-446655440000#aboutABBA,其中“#aboutABBA”可能是一个HTML片段标识符,指向一个id属性为“aboutABBA”的元素。

Blob URI的源

一个Blob URI必须是调用URL.createObjectURL的脚本的Blob URI必须仅在该下有效。

Blob URI的生命周期

本规范针对Blob URI定义了以下生命周期状态:

  1. 本规范添加了一个额外的卸载文档时的清理步骤。用户代理必须将当前文档中调用URL.createObjectURL创建的Blob URI全部注销。如果这些Blob URI已经被提取,则用户代理必须以500 Error Condition作为响应。
  2. 在将一个Blob URI作为参数调用了URL.revokeObjectURL之后,用户代理必须保证该Blob URI被注销。URL.revokeObjectURL被调用之后,当提取该Blob URI时,用户代理必须以500 Error Condition作为响应。

Blob URI的提取模型

用户代理必须仅支持GET请求[HTTP]。如果Blobtype属性,或者Blob是通过对contentType调用slice而创建的,则提取该Blob URI的响应必须包含HTTP的Content-Type头[HTTP],该头的值由type属性或者contentType参数指定。特别地,响应必须仅支持等价于以下HTTP[HTTP]响应的子集:

200 OK

当请求以blob:命名,以GET方式发起,满足源相关的要求,满足生命周期相关的要求,且请求成功时,必须使用该响应[HTTP]。如果使用了该响应码,则用户代理必须提供一个Content-Type头[HTTP],其值等于Blob对象的type属性。参见blob:协议示例章节。

500 Error Condition

当满足以下条件时,必须使用该响应[HTTP]:

  • 提取URL时使用了除GET以外的方法。
  • 请求违反了源相关的要求。在此情况下,在500响应中应当配有说明原因的响应文本,如“500 Expired URI”。
  • 请求违反了生命周期相关的要求。在此情况下,在500响应中应当配有说明原因的文本,如“500 Expired URI”。
  • 底层的资源已经被修改、移动或者删除,或者变为不合法。在此情况下,在500响应中应当配有说明原因的文本,如“500 Invalid Resource”。
  • 底层资源的权限拒绝访问。在此情况下,在500响应中应当配有说明原因的文本,如“500 Access Violation”。
  • 发生了安全错误。在此情况下,在500响应中应当配有说明原因的文本,如“500 Security Violation”。

该响应可以在响应中伴随额外的消息,用于指示Blob资源无法获取的原因。参见blob:协议示例章节

500 Error Condition提供了一个响应吗,而非一个固定的状态。用户代理可以简单地保持“500 Error Condition”或者提供额外的状态信息(如“500 Origin Violation”)。强烈鼓励实现者在响应码以外向开发者提供消息。

请求及响应头

本章节提供了Web应用和用户代理之间使用blob:协议交互的示例。请求可以由类似<img src="blob:550e8400-e29b-41d4-a716-446655440000">的HTML标签触发,在Web应用调用URL.createObjectURL并传递一个Blob后,会返回blob:550e8400-e29b-41d4-a716-446655440000以提取该Blob。这些示例仅仅对协议本身进行说明。虽然Web开发者一般不会与所有的头进行交互,但如果使用XMLHttpRequestgetAllResponseHeaders()方法,则会显示相关的响应头[XHR2]。

响应可能如下所示:

HEADERS

    GET 550e8400-e29b-41d4-a716-446655440000
    
如果<code>[http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob Blob]</code>通过<code>[http://dev.w3.org/2006/webapi/FileAPI/#dfn-type type]</code>属性与一个媒体类型[[http://dev.w3.org/2006/webapi/FileAPI/#RFC2046 RFC2046]]相关联,则响应消息应当包含HTTP的Content-Type头[[http://dev.w3.org/2006/webapi/FileAPI/#HTTP HTTP]]。参考[http://dev.w3.org/2006/webapi/FileAPI/#processing-media-types 处理媒体类型]章节。

HEADERS

    200 OK
    Content-Type: image/jpeg

    ....
    
如果<code>[http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob Blob]</code>关联有一个[http://dev.w3.org/2006/webapi/FileAPI/#file-error-read 文件错误]或其他类型的错误,则用户代理可以使用[http://dev.w3.org/2006/webapi/FileAPI/#FiveHundredInternalServerError 500 Error Condition]作为响应的消息。当使用除GET以外的方法发起请求时,也应当使用该响应。

HEADERS

    500 Error Condition

    This file cannot be read.
    
</ocde>
处理媒体类型

如果提供了Content-Type头[HTTP](例如<code>Blob关联有一个通过type属性),则用户代理应当使用与媒体类型嗅探规范MIMESNIFF相一致的方法来获取并处理该媒体类型。

创建及注销Blob URI

Blob URI使用暴露在URL对象上的方法创建及注销,全局对象Window[HTTP]及WorkerGlobalScope[Web Workers]对此提供了支持。注销Blob URI即将Blob URI与它其所引用的资源解除关系,当其在注销后被提取时,用户代理必须返回一个500响应。本章节描述了对URL规范[URL API]的初始接口,并提供了用于创建和注销Blob URI的方法。

 partial interface URL {

    static DOMString createObjectURL(Blob blob, optional objectURLOptions options);
    static void revokeObjectURL(DOMString url);
};

dictionary objectURLOptions
{
    boolean oneTimeOnly = false;

};
使用ECMA脚本实现本规范的用户代理必须保证不会暴露URL接口的<code>prototype</code>属性,除非该用户代理同时实现了URL[[http://dev.w3.org/2006/webapi/FileAPI/#URL-API URL API]]规范。也就是说,如果用户代理实现了URL[[http://dev.w3.org/2006/webapi/FileAPI/#URL-API URL API]]规范,则<code>URL.prototype</code>必须被评估为true,否则其必须被评估为false。

// Window implements URL;

// WorkerUtils implements URL;

方法及参数

每次使用一个合法的Blob作为参数调用createObjectURL静态方法时,会返回一个唯一的Blob URI,该URI指代一个被调用的该静态方法所属的URL对象所对应的全局对象的作用域下的非空的Blob

  1. 如果调用该方法时,传递的Blob参数不合法,则用户代理必须返回null
  2. 如果调用该方法时,传递了一个合法Blob参数,则用户代理必须返回一个唯一的Blob URI,该URI可用于提取blob参数。

可选的options字典参数可包含一个名为oneTimeOnly的键,该键的值默认为false,如果设置为true,则在第一次提取该Blob URI后,用户代理必须自动注销该Blob URI,而不需要针对该Blob URI调用revokeBlobURL()

示例

在以下示例中,首先获取一个<code>[http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob Blob]</code>对象的引用(在本例中是一个用户选取的底层文件系统的<code>[http://dev.w3.org/2006/webapi/FileAPI/#dfn-file File]</code>),随后使用该<code>[http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob Blob]</code>对象调用<code>URL.createObjectURL()</code>静态方法。

ECMAScript

    var file = document.getElementById('file').files[0];
    if(file){
      blobURLref = window.URL.createObjectURL(file);
      myimg.src = blobURLref;
    
    // 在显示地调用URL.revokeObjectURL()以前,
    // blobURLref始终可用
    // 并将与document并存
      
    
    }
    
在以下示例中,因为[http://dev.w3.org/2006/webapi/FileAPI/#dfn-oneTimeOnly oneTimeOnly]设置为true,所以不需要调用URL.revokeBlobURL()

ECMAScript

    var file = document.getElementById('file').files[0];
    if(file){
      blobURLref = window.URL.createObjectURL(file, {oneTimeOnly: true});
      myimg.src = blobURLref;
    
    // 以上代码会在第一次使用后自动注销blobURLref
    // 后续引用blobURLref将会返回500错误响应

    }

以一个字符串作为url参数revokeObjectURL静态方法,将注销对应的Blob URI

  1. 如果url指向一个合法Blob,且该Blob与此静态方法所属的URL对象关联的全局对象同源,则当提取url时,用户代理必须返回一个500响应码。
  2. 如果url指向一个不合法Blob,或作为参数的url不是一个Blob URI,或url参数指向的BlobURL对象关联的全局对象不同源,则对该方法的调用不做任何动作。用户代理可以在错误控制台中显示一条消息。

revokeObjectURLurl参数是一个表示Blob URI的字符串。

示例

在以下示例中,window1与window2是2个独立的窗口,但是[http://dev.w3.org/2006/webapi/FileAPI/#same-origin 同源],window2可能是window1内的一个iframe[http://dev.w3.org/2006/webapi/FileAPI/#HTML HTML]。

ECMAScript

    myurl = window1.URL.createObjectURL(myblob);
    window2.URL.revokeObjectURL(myurl);
    
由于window1和window2是[http://dev.w3.org/2006/webapi/FileAPI/#same-origin 同源]的,调用URL.revokeObjectURL将保证后续提取myurl会引发一个[http://dev.w3.org/2006/webapi/FileAPI/#FiveHundredInternalServerError 500 Error Condition]响应。</code>

Blob URI创建及注销示例

Blob URI是用于提取<code>Blob的字符串,可以与使用URL.createObjectURL()创建它们的document存活同样久--参考Blob URI的生命周期。如果需要在文档卸载前的清理步骤之前注销它们,Web开发者必须显示地调用URL.revokeObjectURL()来进行注销,或者在创建时通过在options字典中加入oneTimeOnly键指定作一次性使用。

本章节提供了一些创建和注销Blob URI的示例,并配以一定的解释。

示例

在以下示例中,2个Image元素[[http://dev.w3.org/2006/webapi/FileAPI/#HTML HTML]]指向同一个[http://dev.w3.org/2006/webapi/FileAPI/#url Blob URI],该[http://dev.w3.org/2006/webapi/FileAPI/#url Blob URI]创建时通过可选的字典参数里的布尔型键值指定为作一次性使用:

ECMAScript

    var file = document.getElementById('filePicker').files[0];

    var blobURLref = URL.createObjectURL(file, {oneTimeOnly: true});

    img1 = new Image();
    img2 = new Image();

    // 以下赋值语句中,仅一条可用
    // 而另一条在提取时会产生一个500响应


    img1.src = blobURLref;
    img2.src = blobURLref;

对于示例,等价于在每个图片的onload事件处理函数中调用[http://dev.w3.org/2006/webapi/FileAPI/#dfn-revokeObjectURL revokeObjectURL()],不过使用oneTimeOnly这一布尔型的键值可以减少模板式的代码。应当强烈反对多个引用指向一个标记为oneTimeOnly的[http://dev.w3.org/2006/webapi/FileAPI/#url Blob URI]。

注意

在以上用法中,如果用户对图片做显示、隐藏操作,由于赋值的[http://dev.w3.org/2006/webapi/FileAPI/#url Blob URI]的[http://dev.w3.org/2006/webapi/FileAPI/#dfn-oneTimeOnly oneTimeOnly]被设置为true,可能破坏图片。此外,该图片元素不能被克隆,也不能在其src属性赋值后进行读操作。

在以下示例中,显示地调用了[http://dev.w3.org/2006/webapi/FileAPI/#dfn-revokeObjectURL URL.revokeObjectURL()]。

ECMAScript

    var blobURLref = URL.createObjectURL(file);
    img1 = new Image();
    img2 = new Image();

    // 以下2个赋值语句均正常工作

    img1.src = blobURLref;
    img2.src = blobURLref;

    // ... 在body的onload之后
    // 检查是否2个图片都已经加载


    if(img1.complete && img2.complete)
    {
        // 保证后续的引用会返回500响应
        
        URL.revokeObjectURL(blobURLref);
    }
    else {

        msg("Images cannot be previewed!");
        
        // 注销字符串代表的引用
        
        URL.revokeObjectURL(blobURLref);

    }

该示例允许多个引用指向同一个[http://dev.w3.org/2006/webapi/FileAPI/#url Blob URI],但是在图片加载完成后注销了[http://dev.w3.org/2006/webapi/FileAPI/#url Blob URI]。不限制[http://dev.w3.org/2006/webapi/FileAPI/#url Blob URI]的可用次数提供了更大的灵活性的同时,也创建了在使用完毕后还会一直存在的字符串,这在Web应用中特别明显,因为此类应用中document可能存在较长的一段时间。

安全考量

本章节不具规范意义

本规范允许Web内容从底层文件系统读取文件,同时也提供了一套通过唯一标识符访问文件的机制,因此同时也受到一些安全上的考量。本规范也假设主要的用户交互来自于HTML表单中的<input type="file"/>元素[HTML],同时所有由FileReader对象读取的文件都首先来自于用户的选择。因此必须考虑一些重要的安全问题,包括避免恶意文件选取式攻击(选取循环),避免对系统敏感的文件的访问,以及当用户选择的文件被修改时的保护策略。

  • 避免选取循环。在选取文件的过程中,用户可能会面对大量由<input type="file"/>产生的文件选取控件的炮轰式攻击(不断循环要求用户在文件选取控件销毁前“必须选择”某个指定的文件),此时用户代理可通过返回一个大小为0的FileList对象来阻止访问被选的文件。
  • 系统敏感的文件(例如/usr/bin下的文件、密码文件及其它操作系统原生可执行文件)通常不该暴露给Web内容,同样也不该允许Blob URI访问此类文件。当使用同步的读方法时,用户代理可以抛出一个SecurityError异常;使用异步的读方法时,则可以返回一个SecurityError类型的DOMError对象。
  • 当跨域访问一个Blob URI时,会出现针对Blob URI的跨域请求。此时用户代理应当确保在跨域请求上下文中使用500 Error Condition响应。

编辑注解

该章节仅是一个提案,在后续的草案中可能加入更多的安全相关的数据。

需求及用例

本章节描述针对该API的需求,并展示一些用例。该版本的API并无法满足所有的用例,后续版本中可能会解决这些问题。

  • 当用户拥有相应的权限时,用户代理应当提供通过编程方式直接读取和解析本地文件的功能。
    • 示例:歌词浏览器。在一个Web应用中,用户想要从其plist文件的歌曲中读取歌词,用户选取了plist文件,此后文件被打开、读取、解析并以可排序、可操作的列表形式展现给用户。此后用户可以选择一个歌曲,并获取对应的歌词。该过程中用户使用“浏览文件”对话框。
  • 数据应当可以存储在本地以供后续使用,在离线Web应用中访问数据时尤其有用。
    • 示例:日历应用。某用户的公司有一个日历,用户想将本地的事件与公司的日历同步,以标记“忙碌”时段(同时不泄露个人信息)。用户浏览文件并选择了日历文件,该text/calendar文件在浏览器中被解析,这使得用户可以将多个文件合并为一个日历视图。此后用户希望将文件保存回其本地的日历文件(使用“另存为”功能?),同时用户也可以将整合后的日历文件发送回服务器并异步地保存下来。
  • 当提供一定数据以及一个文件名时,用户代理应当提供将之保存为本地文件的功能。
    • 示例:电子表格应用。用户与一个表单进行交互,并产生了一些输入。此后表单将生成CSV(以逗号分隔的变量集)格式的输出以便用户使用“保存...”功能导入到电子表格中。同时该输出还可以直接与基于Web的电子表格应用整合,并异步地上传。
  • 用户代理应当提供一种相比现今基于表单的上传方式更为高效、精简的,通过编程地方式将文件数据发送至远程服务器的功能。
    • 示例:视频或照片上传应用。用户可以选择上传一个大文件,此时文件以“分块传输(chunk-transfered)”的方式发送至服务器。
  • 用户代理应当将提供以上特性的API暴露给脚本。每当与文件系统交互时,都应当通过UI通知用户,使得用户可以完全地取消或中止此类事务。当用户选择文件时会得到相应的通知,并且用户可以取消选择。这些API的调用均不能在没有用户干扰的情况下静默执行。

附录A