Warning:
This wiki has been archived and is now read-only.
File
Contents
介绍
符合规范
依赖关系
术语及算法
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
中的第index
个File
对象。如果集合中不存在第index
个File
对象,则必须返回null。 -
index
必须被用户代理作为FileList
集合中的File
对象的位置来看待,且这个位置的值是从0开始计算的。支持的位置范围从0开始,且比FileList
中File
对象总数小。如果该文件不存在,则它也没有相应的位置#WebIDL。
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
还是代表WorderGlobalScope
,FileReader
都必须可用。
事件句柄特性
下表FileReader
在DOM特性中必须支持的事件句柄特性(以及它们相应的事件类型):
事件句柄特性 | 事件句柄事件类型 |
---|---|
onloadstart | loadstart |
onprogress | progress |
onabort | abort |
onerror | error |
onload | load |
onloadend | loadend |
FileReader的状态
FileReader
对象处于以下三种状态之一。其readyState
特性在被访问时必须返回当前状态所对应的值:
- EMPTY (值为0)
FileReader
对象已经建立,但没有执行任何读取操作。没有读取方法被调用。这正式一个FileReader
对象创建之初的默认状态,该状态会持续到我们调用其读取方法为止。- LOADING (值为1)
- 一个
File
或Blob
正在读取中。一个读取方法正在执行且在读取过程中没有错误发生。 - DONE (值为2)
- 整个
File
或Blob
完全读进内存中,或一个文件错误在读取过程中被触发,或读取过程被abort()函数中断。FileReader
不再读取File
或Blob
。如果readyState
被设置为DONE
那么至少有一个读取方法被FileReader
调用过了。
读取一个File或Blob
多种读取方式
FileReader
接口提供了四种异步读取方法:readAsArrayBuffer
、readAsBinaryString
、readAsText
和readAsDataURL
,这些方法会将文件读取到内存中。如果同一个FileReader
对象同时并发多个读取方法,则用户代理必须在每个读取方法执行时,待readyState
的值为LOADING
后,抛出InvalidStateError
异常#DOMCore。
result特性
我们在访问result
特性时,会得到一个Blob
的数据,其格式或为DOMString
、或为ArrayBuffer
#TypedArrays或为null
,这取决于FileReader
对象调用的读取方法以及可能出现的各种错误。访问该特性还可以得到Blob数据片段,即内存中的File
或Blob
数据的一部分。当读取操作readAsBinaryString
或readAsText
正在进行中时,这个Blob数据片段是一个DOMString
;当读取操作readAsArrayBuffer
正在进行中时,这个Blob数据片段就是一个ArrayBuffer
对象#TypedArrays。(more bytes loaded
(a portion of the total
))#ProgressEvents。下列列表是result
特性的规范性一致性标准:
- 取值时,如果
readyState
的值是EMPTY
(没有调用任何读取方法),则result
特性必须返回null
。 - 取值时,如果读取
File
或Blob
出错,则result
特性必须返回null
。 - 取值时,如果使用的方法是
readAsDataURL
,则result
特性必须返回一个DOMString
,其值是这个File
或Blob
的数据的DataURL编码#DataURL。 - 取值时,如果使用的方法是
readAsBinaryString
并且在读取过程中没有出错,则result
特性必须返回一个代表这个File
或Blob
的二进制字符串的DOMString
。该二进制字符串即每个字节代表一个整形范围。在读取过程中取值时,result
特性也应该以二进制字符串的DOMString
不断返回Blob数据片段。用户代理必须返回至少一个这样的result
,最后在读取完成之后返回一个完整的result
。 - 取值时,如果使用的方法是
readAsText
并且在读取过程中没有出错,则result
特性必须返回一个代表这个File
或Blob
的文本的DOMString
。该文本需要以特定的解码方式在内存中解码。在读取过程中取值时,result
特性也应该以文本的DOMString
不断返回Blob数据片段。用户代理必须返回至少一个这样的result
,最后在读取完成之后返回一个完整的result
。详见以给定的编码方式获取Blob数据片段的备忘。 - 取值时,如果使用的方法是
readAsArrayBuffer
并且在读取过程中没有出错,则result
特性必须返回一个ArrayBuffer
#TypedArrays对象。在读取过程中取值时,result
特性应该返回一个ArrayBuffer
#TypedArrays形式的Blob数据片段。用户代理必须返回至少一个这样的result
,最后在读取完成之后返回一个完整的result
。
result
特性必须返回一个非空(non-null)值,仅在过程事件#ProgressEvents触发之后,所有的读取访问Blob
数据都是异步的。更新result
为Blob
数据的任务都是排队进行的。readAsBinaryString(blob)方法
当readAsBinaryString(blob)
方法被调用时,用户代理必须依次执行下面的步骤(除非另有说明)。
- 如果
readyState
的值是LOADING
,则抛出一个InvalidStateError
异常#DOMCore并终止运行。注意:readAsBinaryString()
方法会由于此算法终止运行而立刻返回。 - 如果读取参数
blob
时出错,则将readyState
的值设为DONE
同时把result
设为null
。然后运行下面的错误处理。- 触发一个名为
error
的过程事件。设置error
特性的读取操作必须是一个DOMError
对象,这个对象可以表明文件出错的类型。 - 触发一个名为
loadend
的过程事件。 - 终止运行。注意:
readAsBinaryString()
方法会由于此算法终止运行而立刻返回。
- 触发一个名为
- 如果没有错误出现,则设置
readyState
的值为LOADING
- 触发一个名为
loadstart
的过程事件。 - 返回
readAsBinaryString()
方法,但继续执行下面的算法步骤。 - 生成进度提醒。
- 在读取时,一旦从
blob
而来的数据成为可用数据,用户代理应该在读取完成之前,不断伴随触发progress
事件,把Blob数据片段更新到result
中,并且这些任务是被排队触发执行的。在向result
特性取值时,得到的是现已载入字节数的Blob数据片段(作为总数的其中一部分)#ProgressEvents,用户代理必须以二进制字符串的格式,返回至少一个这样的result
。最后一次返回的值即为完整的读取结果。 - 当
blob
全部读入内存之后,将readyState
的值设为DONE
- 终止运行。
readAsBinaryString()
方法,并推荐用户代理支持readAsArrayBuffer(blob)
方法替换掉readAsBinaryString()
。readAsDataURL(blob)方法
当readAsDataURL(blob)
方法被调用时,用户代理必须依次执行下面的步骤(除非另有说明)。
- 如果
readyState
的值是LOADING
,则抛出一个InvalidStateError
异常#DOMCore并终止运行。注意:readAsDataURL()
方法会由于此算法终止运行而立刻返回。 - 如果读取参数
blob
时出错,或如果用户代理的URL长度限制阻止了Data URL#DataURL数据的返回,则将readyState
的值设为DONE
同时把result
设为null
。然后运行下面的错误处理。- 触发一个名为
error
的过程事件。设置error
特性的读取操作必须是一个DOMError
对象,这个对象可以表明文件出错的类型。 - 触发一个名为
loadend
的过程事件。 - 终止运行。注意:
readAsDataURL()
方法会由于此算法终止运行而立刻返回。
- 触发一个名为
- 如果没有错误出现,则设置
readyState
的值为LOADING
- 触发一个名为
loadstart
的过程事件。 - 返回
readAsDataURL()
方法,但继续执行下面的算法步骤。 - 生成进度提醒。
- 将一个任务排队,这个任务待其
blob
被完全读入内存之后,以DataURL#DataURL格式更新到result
特性中。当取值时,result
特性返回完整的blob
的Data URL#DataURL数据。 - 将
readyState
的值设为DONE
- 触发一个名为
load
的过程事件。 - 触发一个名为
loadend
的过程事件。 - 终止运行。
readAsText(blob, encoding)方法
当readAsText(blob, encoding)
方法被调用时(encoding参数是可选的),用户代理必须依次执行下面的步骤(除非另有说明)。
- 如果
readyState
的值是LOADING
,则抛出一个InvalidStateError
异常#DOMCore并终止运行。注意:readAsText()
方法会由于此算法终止运行而立刻返回。 - 如果读取参数
blob
时出错,则将readyState
的值设为DONE
同时把result
设为null
。然后运行下面的错误处理。- 触发一个名为
error
的过程事件。设置error
特性的读取操作必须是一个DOMError
对象,这个对象可以表明文件出错的类型。 - 触发一个名为
loadend
的过程事件。 - 终止运行。注意:
readAsText()
方法会由于此算法终止运行而立刻返回。
- 触发一个名为
- 如果没有错误出现,则设置
readyState
的值为LOADING
- 触发一个名为
loadstart
的过程事件。 - 返回
readAsText()
方法,但继续执行下面的算法步骤。 - 生成进度提醒。
- 在读取时,一旦从
blob
而来的数据成为可用数据,用户代理应该在读取完成之前,不断伴随触发progress
事件,把Blob数据片段更新到result
中,并且这些任务是被排队触发执行的。在向result
特性取值时,得到的是现已载入字节数的Blob数据片段(作为总数的其中一部分)#ProgressEvents,用户代理必须以指定编码格式的文本,返回至少一个这样的result
。最后一次返回的值即为完整的读取结果。 - 当
blob
全部读入内存之后,将readyState
的值设为DONE
- 终止运行。
result
返回的必须是0x30D1且长度为1,而不是0x30D1 0xFFFD且长度为2。即使结尾的第五字节E3 83并不是合法代码点在UTF-8下,用户代理禁止返回一个代表错误代码点的U+FFFE作为result
的返回值。readAsArrayBuffer(blob)方法
当readAsArrayBuffer(blob)
方法被调用时,用户代理必须依次执行下面的步骤(除非另有说明)。
- 如果
readyState
的值是LOADING
,则抛出一个InvalidStateError
异常#DOMCore并终止运行。注意:readAsArrayBuffer()
方法会由于此算法终止运行而立刻返回。 - 如果读取参数
blob
时出错,则将readyState
的值设为DONE
同时把result
设为null
。然后运行下面的错误处理。- 触发一个名为
error
的过程事件。设置error
特性的读取操作必须是一个DOMError
对象,这个对象可以表明文件出错的类型。 - 触发一个名为
loadend
的过程事件。 - 终止运行。注意:
readAsArrayBuffer()
方法会由于此算法终止运行而立刻返回。
- 触发一个名为
- 如果没有错误出现,则设置
readyState
的值为LOADING
- 触发一个名为
loadstart
的过程事件。 - 返回
readAsArrayBuffer()
方法,但继续执行下面的算法步骤。 - 生成进度提醒。
- 在读取时,一旦从
blob
而来的数据成为可用数据,用户代理应该在读取完成之前,不断伴随触发progress
事件,把Blob数据片段更新到result
中,并且这些任务是被排队触发执行的。在向result
特性取值时,得到的是现已载入字节数的Blob数据片段(作为总数的其中一部分)#ProgressEvents,用户代理必须以ArrayBuffer 格式#TypedArrays的文本,返回至少一个这样的result
。最后一次返回的值即为完整的读取结果。 - 当
blob
全部读入内存之后,将readyState
的值设为DONE
- 终止运行。
abort()方法
当abort()
方法被调用时,用户代理必须依次执行下面的步骤:
- 如果
readyState
的值是EMPTY
或DONE
,则设置result
特性为null
并终止运行剩余的步骤,不再做任何处理。 - 如果
readyState
的值是LOADING
则设置readyState
的值为DONE
,同时设置result
为null
。 - 移除任务队列中由此
FileReader
对象引发的所有任务。 - 终止所有正在进行的读取方法。
- 触发一个名为
abort
的过程事件 - 触发一个名为
loadend
的过程事件 - 终止运行
Blob参数
本规范中有多个方法都强制要求传入Blob
参数。
blob
- 这是一个
FileReader
的四个异步读取方法以及FileReaderSync
的四个同步读取方法都会所调用的Blob
参数。该参数必须是一个FileList
中的单个File
,或者是没有从文件系统获取,而是在当前方法下创建的Blob
对象。
编码类型判定
当使用readAsText()
方法读取blob
对象时,可选参数encoding
必须是一个字符集的名称或别名#IANACHARSET,否则均视为无效。下面是编码类型判定必须遵循的步骤:
- 按照
encoding
参数表示的类型对blob
进行解码,如果该参数已提供且有效,则不必进行接下来的其它步骤。如果该参数无效,或未提供,或用户代理无法判定编码类型,则进入下一步。 - 使用参数
blob
的type
特性作为字符参数#RFC2046进行解码,如果该type
特性不存在,或者其提供的并不是可用的互联网字符集,则进入下一步。 - 使得字符集为null
- 从第一行开始,跟下面的表格中的每一行往后匹配,如果
blob
的开头的几个字节匹配第一列列出的内容,则使用其对应的编码类型。如果仍未匹配,则字符集仍未null。
Bytes in Hexadecimal | Description |
---|---|
FE FF | UTF-16BE BOM |
FF FE | UTF-16LE BOM |
EF BB BF | UTF-8 BOM |
- 如果字符集仍未null则设字符集为UTF-8
- 返回按最终字符集的类型解码
blob
。对FileReader
对象的result
特性取值时、用FileReaderSync
对象调用readAsText
方法时,都返回该字符集格式的字符串。将使用当前字符集下无效的字节或字节序列替换为单个U+FFFD字符#Unicode。当处理Blob数据片段的时候,如果可用的话,使用encoding caveat。
事件
本规范提及的读取方法的运行进度提醒,均必须遵循下列步骤:
- 当读取操作进行时,在
FileReader
对象上排队执行触发一个名为progress
的过程事件,事件每50ms或每字节触发一次,以较少者为准。在load
事件出发之前至少要触发一个progress
事件,即在100%完成读取操作时。如果100%blob
在50ms之内就被读取进了内存,用户代理必须触发一个名为progress
的过程事件以示完成。 - 当
blob
数据完全读取进入内存后,在FileReader
对象上排队执行触发一个名为load
的事件。 - 当
blob
数据完全读取进入内存后,在FileReader
对象上排队执行触发一个名为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 | 当读取请求完成时(不论成功还是失败) |
事件之间的规律
以下是本规范中异步读取方法触发的事件的规范性规律。
- 一旦
loadstart
已经被触发,则一个相应的loadend
会在读取操作完成时触发,除非读取方法被abort()
取消同时一个新的读取方法已经被调用。 - 在
blob
完成读入内存后,一个progress
事件将被触发。 -
loadstart
事件之前没有progress
事件。 -
abort
、load
、loadend
事件之后没有progress
事件。 -
loadend
事件之后没有abort
事件、load
事件、error
事件。
loadstart
事件和loadend
事件并不是以一对一的方式耦合的。在线程上进行读操作
在Web Workers生成的线程上进行读操作并不会阻塞主线程,因此用户可以对File
及Blob
接口的读取API进行同步调用。本章节定义可在Workers[Web Workers]中使用的同步API。Workers可以同时利用异步API(FileReader
对象)和同步API(FileReaderSync
对象)。
FileReaderSync
接口
该接口提供对将File
及Blob
对象'同步地读入'内存的方法。
[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
参数可选)时,必须执行以下步骤:
- 如果读取
blob
参数的过程中发生错误,则抛出合适的异常,并终止所有步骤。 - 如果未发生任何错误,则将
blob
读入内存,使用编码检测算法获取blob
的数据内容并返回。
readAsDataURL
方法
当调用readAsDataURL(blob)
方法时,必须执行以下步骤:
- 如果读取
blob
参数的过程中发生错误,则抛出合适的异常,并终止所有步骤。 - 如果未发生任何错误,则将
blob
读入内存,并以Data URL[DataURL]的形式返回blob
的数据内容:
readAsArrayBuffer
方法
当调用readAsArrayBuffer方法(blob)
方法时,必须执行以下步骤:
- 如果读取
blob
参数的过程中发生错误,则抛出合适的异常,并终止所有步骤。 - 如果未发生任何错误,则将
blob
读入内存,并以ArrayBuffer
[强类型数组]的形式返回blob
的数据内容。
错误及异常
在从底层文件系统读取文件时,可能出现错误状态。以下列出了可能发生错误的情况,但不具规范意义。
- 当某次调用异步的读取方法或同步的读取方法时,访问的
File
或Blob
并不存在。这种情况的出现可能是因为针对资源的引用创建之后,资源被移动或删除了(例如其他应用并发地修改了资源)。此时参见NotFoundError章节。 -
File
或Blob
不可读。这种情况的出现可能是因为针对资源的引用创建之后,出现了权限问题(例如其他应用对资源加了并发锁)。此时参见NotReadableError章节。 - 在Web应用中,用户代理可能将某些文件鉴定为不安全的。这可能是在用户选择了文件之后,文件又在磁盘上被修改了,导致不合法的读取。另外有些文件或文件夹可能被底层文件系统加以限制,试图读取此类资源会被认为违反了安全策略。关于安全问题详见安全考量章节。此类问题出现时参见SecurityError章节。
- 对Web应用的数据结构而言,某些文件体积可能过大。例如用户代理针对Data URL有URL的长度限制,此时可能导致无法以Data URL编码的形式返回大文件[DataURL]。此时参见EncodingError章节。
抛出异常或返回错误
本章节不具规范意义。
当读取文件时,可能出现错误状态。
当读取文件发生错误时,同步的读方法需要按下表的描述抛出相应的异常,否则必须从下表中选择一个最合适的DOMError对象[DOMCore],且FileReader
的error
属性必须返回该对象,对于其他情况则返回null。
错误类型 | 描述 |
NotFoundError
|
当进行读操作时,无法找到File 或Blob 资源,则如果使用的是异步读方法,error属性必须返回一个"NotFoundError "DOMError对象;而对于同步读方法,则必须抛出一个NotFoundError 异常。
|
SecurityError | 如果:
则如果使用的是异步读方法,error属性可以返回一个" |
NotReadableError
|
如果File 或Blob 不可读,则如果使用的是异步读方法,error属性必须返回一个"NotReadableError "DOMError对象;而对于同步读方法,则必须抛出一个NotReadableError 异常。这通常是因为针对资源的引用创建之后,出现了权限问题(例如其他应用对资源加了并发锁)。
|
EncodingError
|
如果针对Data URL的URL长度限制导致File 或Blob 无法以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开发者有直观的吸引力。
- 根据上文提到的用例,可以重新提议HTTP作为大纲。HTTP已经有了一个定义良好的请求-响应模型,并且已经在Web应用中使用。但是
Blob
资源通常存放在“内存中”(例如文件被读取进内存后),此时资源并不像“传统的”HTTP资源需要通过DNS提取。虽然有些代理自动将本地计算机的底层文件系统通过一个HTTP服务器(例如类似http://localhost的URL)“代理”出来,但HTTP传统上并非用于本地资源。更进一步,该规范提出的一个重要用例是需要可以通过API调用来撤销此类URI,但是HTTP的URI通常用于更加持久(自然不会是以存在于内存中的资源为主,如Web应用可以读取的文件),因此如果重用HTTP大纲,可能导致已经拥有良好的实践的Web开发者产生迷惑。 - 如果重用文件URI,则需要引入相应的改变,例如添加响应码。由于在Web应用中使用的方法与现有文件大纲不同,URI的结构可能需要调整,同时需要在现有类似ad-hoc的行为上正常工作的内容上,再叠加请求-响应行为。鉴于文件URI已经在历史中被应用,为了上文引用的用例而对其进行修改显得过于草率,并且Blob URI大纲的用例同时有在文件系统下以外使用的需求。
- 可以使用类似urn:uuid的大纲,urn:uuid大纲是非常通用的,因此值得重新提议,虽然在HTTP和Javascript构成的Web应用中使用此类大纲是史无前例的。在urn:uuid大纲之下的URI有一个缺点,即在Web平台上,开发者并不熟悉,并会引入不一致性,而一个全新的大纲则可以针对其关联的内容进行显示地定义。从理论上说,URI并不保证其被提取后会指向哪种类型的资源,这些由内容标记和媒体类型负责。但是在实践中,大纲的名字会建立对资源以及请求-响应事务所用协议的预期,因此选择一个清楚表明主要用途的名字--用命名表达对内存中的
Blob
资源的访问--是一个合适的折中方案,并且能够在Web平台上提供清晰性、熟悉性以及一致性。
Blob URI大纲的定义
本章节使用正式的语法定义blob:
URI大纲。一个blob:
URI由blob:大纲以及一个不透明字符串组成,随后跟随一个可选的片断标识符。在本规范中不透明字符串是通过启发式方法生成的具有唯一性的字符串,其要求是2个串相似的机率很小,并且很难通过猜测得到。(例如由[RFC4122]定义的全球唯一标识符(UUID)可用作不透明字符串]])。一个片段标识符是可选的,当使用时,根据Blob
或File
资源的媒体类型,有不同的释义,参考[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定义了以下生命周期状态:
- 本规范添加了一个额外的卸载文档时的清理步骤。用户代理必须将当前文档中调用
URL.createObjectURL
创建的Blob URI全部注销。如果这些Blob URI已经被提取,则用户代理必须以500 Error Condition作为响应。 - 在将一个Blob URI作为参数调用了
URL.revokeObjectURL
之后,用户代理必须保证该Blob URI被注销。URL.revokeObjectURL
被调用之后,当提取该Blob URI时,用户代理必须以500 Error Condition作为响应。
Blob URI的提取模型
用户代理必须仅支持GET请求[HTTP]。如果Blob
有type
属性,或者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开发者一般不会与所有的头进行交互,但如果使用XMLHttpRequest
的getAllResponseHeaders()
方法,则会显示相关的响应头[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>
处理媒体类型
关联有一个通过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
。
- 如果调用该方法时,传递的
Blob
参数不合法,则用户代理必须返回null
。 - 如果调用该方法时,传递了一个合法的
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。
- 如果
url
指向一个合法的Blob
,且该Blob
与此静态方法所属的URL对象关联的全局对象同源,则当提取url
时,用户代理必须返回一个500响应码。 - 如果
url
指向一个不合法的Blob
,或作为参数的url
不是一个Blob URI,或url
参数指向的Blob
与URL对象关联的全局对象不同源,则对该方法的调用不做任何动作。用户代理可以在错误控制台中显示一条消息。
revokeObjectURL
的url
参数是一个表示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创建及注销示例
的字符串,可以与使用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的调用均不能在没有用户干扰的情况下静默执行。