通常,Web 应用程序会提示用户选择一个文件,通常是为了将其上传到服务器。除非 Web 应用程序使用插件,否则文件选择将通过 HTML 输入元素完成,例如 <input type="file"/>
。Firefox 3.6 现在支持大部分 W3C File API,它指定了将选定文件异步读取到内存中的能力,并在 Web 应用程序中对文件数据执行操作(例如,在上传之前显示图像的缩略图预览,或查找 MP3 文件中的 ID3 标签,或查找 JPEG 文件中的 EXIF 数据,所有这些都在客户端进行)。这是一个新的 API,它取代了 Firefox 3 中引入的文件 API。
需要注意的是,即使在 W3C File API 草案出现之前(该草案直到 2009 年 11 月才成为工作草案),Firefox 3 及更高版本也提供 将文件同步读取到内存中的能力,但该功能应被认为是已弃用的,因为它将被 Firefox 3.6 中新实现的 异步 File API 取代。已弃用的 API 允许您同步访问文件
// After obtaining a handle to a file
// access the file data
var dataURL = file.getAsDataURL();
img.src = dataURL;
虽然 Firefox 3.6 将继续支持上述代码用法,但它应该被认为是已弃用的,因为它在主线程上同步读取文件。对于大型文件,这会导致阻塞读取结果,这是不可取的。此外,文件对象本身提供了一个方法来从中读取,而不是拥有一个单独的阅读器对象。这些考虑因素决定了 Firefox 3.6 中新的 File API(以及 规范)的技术方向。本文的其余部分将介绍新引入的 File API。
访问文件选择
Firefox 3.6 支持在输入元素上进行多个文件选择,并使用 FileList 接口返回所有选定的文件。以前版本的 Firefox 仅支持使用输入元素选择一个文件。此外,FileList 接口也作为 DataTransfer 接口的属性暴露给 HTML5 拖放 API。用户也可以将多个文件拖放到网页内的拖放目标上。
以下 HTML 代码会生成标准的文件选择器,您可以使用它选择多个文件
请注意,如果您不使用 multiple
属性,则只能启用单个文件选择。
您可以通过遍历 FileList 来处理通过文件选择器(使用 input
元素)或通过 DataTransfer 对象获得的所有选定文件
var files = document.getElementById("inputFiles").files;
// or, for a drag event e:
// var dt = e.dataTransfer; var files = dt.files
for (var i = 0; i < files.length; i++) {
var file = files[i];
handleFile(file);
}
文件的属性
从 FileList 中获取对单独选定文件的引用后,您将获得一个 File 对象,它具有 name
、type
和 size
属性。继续上面的代码片段
function handleFile(file) {
// RegExp for JPEG mime type
var imageType = /image/jpeg/;
// Check if match
if (!file.type.match(imageType)) {
return false;
}
// Check if the picture exceeds set limit
if(file.size > maxSize) {
alert("Choose a smaller photo!");
return false;
}
// Add file name to page
var picData = document.createTextNode(file.name);
dataGrid.appendChild(picData);
return true;
}
size
属性是以字节为单位的文件大小。name
属性是文件的名称,不含路径信息。type
属性是一个 ASCII 编码的字符串,以小写字母表示文件的媒体类型,表示为 RFC2046 MIME 类型。type
属性特别有用,因为它可以用于嗅探文件类型,如上面的示例所示,其中脚本确定该文件是否为 JPEG 文件。如果 Firefox 3.6 无法确定文件的 type
,它将返回空字符串。
读取文件
Firefox 3.6 及更高版本支持 FileReader 对象,该对象使用事件回调来标记进度,将文件数据异步读取到内存中。该对象以标准方式实例化
var binaryReader = new FileReader();
事件处理程序属性用于处理文件读取操作的 result
。对于非常大的文件,可以观察文件被读取到内存中的进度事件(使用 onprogress
事件处理程序属性来设置事件处理程序函数)。这在以下情况下非常有用:所讨论的驱动器可能不是本地硬件,或者所讨论的文件特别大。
FileReader 对象支持三种方法将文件读取到内存中。每种方法都允许以不同的格式对文件数据进行编程访问,但在实践中,应该只对给定的 FileReader 对象调用一种读取方法
filereader.readAsBinaryString(file);
将异步返回一个二进制字符串,其中每个字节都由 [0..255] 范围内的整数表示。这对于对文件数据的二进制操作很有用,例如,查找 MP3 文件中的 ID3 标签,或查找 JPEG 图像中的 EXIF 数据。filereader.readAsText(file, encoding);
将异步返回一个字符串,该字符串的格式由编码参数(例如encoding = "UTF-8"
)指定。这对于处理文本文件很有用,例如,解析 XML 文件。filereader.readAsDataURL(file);
将异步返回一个 数据 URL。Firefox 3.6 允许使用大型 URL,因此此功能在 URL 可以帮助在网页中显示媒体内容时特别有用,例如,用于图像数据、视频数据或音频数据。
一个示例可以帮助将所有这些联系在一起
if (files.length > 0) {
if (!handleFile(files[0])) {
invalid.style.visibility="visible";
invalid.msg = "Select a JPEG Image";
}
}
var binaryReader = new FileReader();
binaryReader.onload = function(){
var exif = findEXIFInJPG(binaryReader.result);
if (!exif) {
// ...set up conditions for lack of data
}
else {
// ...write out exif data
}
binaryReader.onprogress = updateProgress;
binaryReader.onerror = errorHandler;
binaryReader.readAsBinaryString(file);
function updateProgress(evt){
// use lengthComputable, loaded, and total on ProgressEvent
if (evt.lengthComputable) {
var loaded = (evt.loaded / evt.total);
if (loaded < 1) {
// update progress meter
progMeter.style.width = (loaded * 200) + "px";
}
}
}
function errorHandler(evt) {
if(evt.target.error.code == evt.target.error.NOT_FOUND_ERR) {
alert("File Not Found!");
}
}
为了处理二进制数据,使用字符串上公开的 charCodeAt 函数将特别有用。例如,以下实用程序
function getByteAt(file, idx) {
return file.charCodeAt(idx);
}
允许提取给定索引处字符的 Unicode 值。
可以在 Paul Rouget 关于 File API 的精彩演示 中找到在 Firefox 3.6 中类似代码的实际示例,其中包括使用 readAsDataURL
方法呈现图像以及对 JPEG 进行二进制分析以进行 EXIF 检测(使用 readAsBinaryString
方法)。
关于规范的一句话
存在 File API 的 W3C 公共工作草案,这预示着其他浏览器将在不久的将来实现它。Firefox 3.6 的实现相当完整,但缺少规范中提到的某些技术。值得注意的是,urn
特性在 File 对象中尚未实现,使用 slice
方法提取文件的字节范围的能力也没有实现。尚未在 Worker 线程 中实现同步读取文件的方法。这些功能将在 Firefox 的未来版本中提供。
参考文献
38 条评论