目录
4 File API
File API 在表单中的文件输入字段的基础上,又添加了一些直接访问文件信息的接口。HTML5 在 DOM 中为文件输入元素添加了一个 files 集合。在通过文件输入字段选择了一或多个文件时,files 集合中将包含一组 File 对象,每个 File 对象对应着一个文件。每个 File 对象都有下列只读属性。
name:本地文件系统中的文件名。
size:文件的字节大小。
type:字符串,文件的 MIME 类型。
lastModifiedDate:字符串,文件上一次被修改的时间(只有 Chrome 实现了这个属性)。
举个例子,通过侦听 change 事件并读取 files 集合就可以知道选择的每个文件的信息:
var filesList = document.getElementById("files-list");
EventUtil.addHandler(filesList, "change", function(event){
var files = EventUtil.getTarget(event).files,
i = 0,
len = files.length;
while (i < len){
console.log(files[i].name + " (" + files[i].type + ", " + files[i].size +
" bytes) ");
i++;
}
});
4.1 FileReader类型
FileReader类型实现的是一种异步文件读取机制。可以把FileReader想象成XMLHttpRequest, 区别只是它读取的是文件系统,而不是远程服务器。为了读取文件中的数据,FileReader 提供了如下 几个方法。
readAsText(file,encoding):以纯文本形式读取文件,将读取到的文本保存在 result 属 性中。第二个参数用于指定编码类型,是可选的。
readAsDataURL(file):读取文件并将文件以数据 URI 的形式保存在 result 属性中。
readAsBinaryString(file):读取文件并将一个字符串保存在 result 属性中,字符串中的 每个字符表示一字节。
readAsArrayBuffer(file):读取文件并将一个包含文件内容的 ArrayBuffer 保存在 result 属性中。
文件成功加载后会触发 load 事件;如果发生了 error 事件,就不会发生 load 事件。以下是一个 使用上述三个事件的例子。
var filesList = document.getElementById("files-list");
EventUtil.addHandler(filesList, "change", function(event){
var info = "",
output = document.getElementById("output"),
progress = document.getElementById("progress"),
files = EventUtil.getTarget(event).files,
type = "default",
reader = new FileReader();
if (/image/.test(files[0].type)){
reader.readAsDataURL(files[0]);
type = "image";
} else {
reader.readAsText(files[0]);
type = "text";
}
reader.onerror = function(){
output.innerHTML = "Could not read file, error code is " +
reader.error.code;
};
reader.onprogress = function(event){
if (event.lengthComputable){
progress.innerHTML = event.loaded + "/" + event.total;
}
};
reader.onload = function(){
var html = "";
switch(type){
case "image":
html = "<img src=\"" + reader.result + "\">";
break;
case "text":
html = reader.result;
break;
}
output.innerHTML = html;
};
});
4.2 读取部分内容
有时候,我们只想读取文件的一部分而不是全部内容。为此,File 对象还
var filesList = document.getElementById("files-list");
EventUtil.addHandler(filesList, "change", function(event){
var info = "",
output = document.getElementById("output"),
progress = document.getElementById("progress"),
files = EventUtil.getTarget(event).files,
reader = new FileReader(),
blob = blobSlice(files[0], 0, 32);
if (blob){
reader.readAsText(blob);
reader.onerror = function(){
output.innerHTML = "Could not read file, error code is " +
reader.error.code;
};
reader.onload = function(){
output.innerHTML = reader.result;
};
} else {
alert("Your browser doesn' t support slice().");
}
});
支持一个 slice()方法, 这个方法在 Firefox 中的实现叫 mozSlice(),在 Chrome 中的实现叫 webkitSlice(),Safari 的 5.1 及 之前版本不支持这个方法。slice()方法接收两个参数:起始字节及要读取的字节数。这个方法返回一 个 Blob 的实例,Blob 是 File 类型的父类型。下面是一个通用的函数,可以在不同实现中使用 slice() 方法:
function blobSlice(blob, startByte, length){
if (blob.slice){
return blob.slice(startByte, length);
} else if (blob.webkitSlice){
return blob.webkitSlice(startByte, length);
} else if (blob.mozSlice){
return blob.mozSlice(startByte, length);
} else {
return null;
}
}
Blob 类型有一个 size 属性和一个 type 属性,而且它也支持 slice()方法,以便进一步切割数 据。通过 FileReader 也可以从 Blob 中读取数据。下面这个例子只读取文件的 32B 内容。
4.3 对象URL
对象 URL 也被称为 blob URL,指的是引用保存在 File 或 Blob 中数据的 URL。使用对象 URL 的 好处是可以不必把文件内容读取到 JavaScript 中而直接使用文件内容。为此,只要在需要文件内容的地 方提供对象 URL 即可。要创建对象 URL,可以使用 window.URL.createObjectURL()方法,并传入 File 或 Blob 对象。这个方法在 Chrome 中的实现叫 window.webkitURL.createObjectURL(),因 此可以通过如下函数来消除命名的差异:
function createObjectURL(blob){
if (window.URL){
return window.URL.createObjectURL(blob);
} else if (window.webkitURL){
return window.webkitURL.createObjectURL(blob);
} else {
return null;
}
}
这个函数的返回值是一个字符串,指向一块内存的地址。因为这个字符串是 URL,所以在 DOM 中 也能使用。例如,以下代码可以在页面中显示一个图像文件:
var filesList = document.getElementById("files-list");
EventUtil.addHandler(filesList, "change", function(event){
var info = "",
output = document.getElementById("output"),
progress = document.getElementById("progress"),
files = EventUtil.getTarget(event).files,
reader = new FileReader(),
url = createObjectURL(files[0]);
if (url){
if (/image/.test(files[0].type)){
output.innerHTML = "<img src=\"" + url + "\">";
} else {
output.innerHTML = "Not an image.";
}
} else {
output.innerHTML = "Your browser doesn't support object URLs.";
}
});
如果不再需要相应的数据,最好释放它占用的内容。但只要有代码在引用对象 URL,内存就不会释 放。要手工释放内存,可以把对象 URL 传给 window.URL.revokeOjbectURL()(在 Chrome 中是 window.webkitURL.revokeObjectURL())。要兼容这两种方法的实现,可以使用以下函数:
function revokeObjectURL(url){
if (window.URL){
window.URL.revokeObjectURL(url);
} else if (window.webkitURL){
window.webkitURL.revokeObjectURL(url);
}
}
4.4 读取拖放的文件
围绕读取文件信息,结合使用 HTML5 拖放 API 和文件 API,能够创造出令人瞩目的用户界面:在页 面上创建了自定义的放置目标之后,你可以从桌面上把文件拖放到该目标。与拖放一张图片或者一个链接 类似,从桌面上把文件拖放到浏览器中也会触发 drop 事件。而且可以在 event.dataTransfer. files 中读取到被放置的文件,当然此时它是一个 File 对象,与通过文件输入字段取得的 File 对象一样。 下面这个例子会将放置到页面中自定义的放置目标中的文件信息显示出来:
var droptarget = document.getElementById( "droptarget");
function handleEvent(event){
var info = "",
output = document.getElementById("output"),
files, i, len;
EventUtil.preventDefault(event);
if (event.type == "drop"){
files = event.dataTransfer.files;
i = 0;
len = files.length;
while (i < len){
info += files[i].name + " (" + files[i].type + ", " + files[i].size +
" bytes)<br>";
i++;
}
output.innerHTML = info;
}
}
EventUtil.addHandler(droptarget, "dragenter", handleEvent);
EventUtil.addHandler(droptarget, "dragover", handleEvent);
EventUtil.addHandler(droptarget, "drop", handleEvent);
与之前展示的拖放示例一样,这里也必须取消 dragenter、dragover 和 drop 的默认行为。在 drop 事件中,可以通过 event.dataTransfer.files 读取文件信息。还有一种利用这个功能的流行 做法,即结合 XMLHttpRequest 和拖放文件来实现上传。
4.5 使用XHR上传文件
这样使用 FormData 类型就很容易做到了(第 21章介绍过 FormData)。首先,要创建一个 FormData 对象,通过它调用 append()方法并传入相应的 File 对象作为参数。然后,再把 FormData 对象传递 给 XHR 的 send()方法,结果与通过表单上传一模一样。
var droptarget = document.getElementById("droptarget");
function handleEvent(event){
var info = "",
output = document.getElementById("output"),
data, xhr,
files, i, len;
EventUtil.preventDefault(event);
if (event.type == "drop"){
data = new FormData();
files = event.dataTransfer.files;
i = 0;
len = files.length;
while (i < len){
data.append("file" + i, files[i]);
i++;
}
xhr = new XMLHttpRequest();
xhr.open("post", "FileAPIExample06Upload.php", true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
alert(xhr.responseText);
}
};
xhr.send(data);
}
}
EventUtil.addHandler(droptarget, "dragenter", handleEvent);
EventUtil.addHandler(droptarget, "dragover", handleEvent);
EventUtil.addHandler(droptarget, "drop", handleEvent);
这个例子创建一个 FormData 对象,与每个文件对应的键分别是 file0、file1、file2 这样的格 式。注意,不用额外写任何代码,这些文件就可以作为表单的值提交。而且,也不必使用 FileReader, 只要传入 File 对象即可。