一. File和Blob
一个Blob对象就是一个包含有只读原始数据的类文件对象。Blob对象中的数据并不一定得是JavaScript中的原生形式。File接口基于Blob,继承了Blob的功能,并且扩展支持了用户计算机上的本地文件。
创建Blob对象的方法有几种,可以调用Blob构造函数,还可以使用一个已有Blob对象上的slice()方法切出另一个Blob对象,还可以调用canvas对象上的toBlob方法。
以上为MDN上官方口吻的解释。实际上,Blob是计算机界通用术语之一,全称写作:BLOB (binary large object),表示二进制大对象。MySql/Oracle数据库中,就有一种Blob类型,专门存放二进制数据。
1.1 Blob构造函数
Blob Blob(
[optional] Array parts,
[optional] BlobPropertyBag properties
)
parts
一个数组,包含了将要添加到Blob对象中的数据。数组元素可以是任意多个的 ArrayBuffer , ArrayBufferView (typed array) , Blob ,或者 DOMString 对象。
properties
一个对象,设置Blob对象的一些属性
比如说下面的代码可以创一个xml格式的数据:
var aFileParts = ['hey!'];
var oMyBlob = new Blob(aFileParts, { "type" : "text/xml" }); // the blob
1.2 canvas.toBlob方法
HTMLCanvasElement.toBlob() 方法创造Blob对象,用以展示canvas上的图片;这个图片文件可以被缓存或保存到本地,由用户代理端自行决定。如不特别指明,图片的类型默认为 image/png,分辨率为96dpi。
使用canvas.toBlob可以将canvas图像转换为文件,下面是代码:
var canvas = document.getElementById("canvas");
canvas.toBlob(function(blob) {
var newImg = document.createElement("img"),
url = URL.createObjectURL(blob);
newImg.onload = function() {
// no longer need to read the blob so it's revoked
URL.revokeObjectURL(url);
};
newImg.src = url;
document.body.appendChild(newImg);
});
1.3 XMLHttpRequest.responseType = Blob
XMLHttpRequest2.0中设置XMLHttpRequest.responseType = Blob,接受到响应时XMLHttpRequest.response是一个blob对象。
function readBlobURL(src, callback) {
var xhr = new XMLHttpRequest();
var blob;
xhr.open("GET", src, true);
xhr.responseType = 'blob';
xhr.onload = function (e) {
if (e.target.status == 200)
return callback(e.target.response); // e.target.response也就是请求的返回就是Blob对象
}
xhr.send();
return blob;
}
比如说在项目中我需要播放服务器上的 .amr 和 .spx 音频文件,html5的audio标签是不支持这两种格式的,怎么办呢?整理下思路,有专门的js库可以解码amr或者spx文件为原始音频数据,然后用AudioContext的api去播放原始音频数据。然而解码函数的入参是ArrayBuffer或者BinaryString格式的音频数据,所以先得把音频文件弄成ArrayBuffer或者BinaryString。刚好FileReader可以把一个Blob对象读成先ArrayBuffer和BinaryString。
噔噔噔噔噔蹬蹬噔,XMLHttpRequest.responseType = Blob闪亮登场!!!直接从服务器把音频文件下载为Blob对象。需求完美解决,可以安心回家陪女朋友~~
1.4 File
File继承于Blob对象,所以本质上是一个只读原始数据的类文件对象。不过呢,file对象多了name、size、type、lastModified属性。
下图是选择一个图片之后console打印的结果:
上图可以清晰的看到name、size、type属性。file对象的构造器是File,File对象继承于Blob。
二. FileReader
使用FileReader对象,web应用程序可以异步的读取存储在用户计算机上的文件(或者原始数据缓冲)内容,可以使用File对象或者Blob对象来指定所要处理的文件或数据。
FileReader有五个方法:
void abort();
//开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个ArrayBuffer对象以表示所读取文件的内容.
void readAsArrayBuffer(in Blob blob);
//result属性中将包含所读取文件的原始二进制数据.
void readAsBinaryString(in Blob blob);
//result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.
void readAsDataURL(in Blob blob);
//result属性中将包含一个字符串以表示所读取的文件内容.
void readAsText(in Blob blob, [optional] in DOMString encoding);
readAsArrayBuffer 返回的是arrayBuffer, readAsBinaryString 返回的是二进制string,可以通过下面的方法转换:
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i
}
return buf;
}
BinaryString通过window.btoa()加密,从而组装成data规范的string。
readAsDataURL 可以很方便的实现图片预览:
//假设file是一个File对象
var image = new Image();
var file = e.target.files[0];
var fileReader = new FileReader();
fileReader.onload = function (oFREvent) {
var data = oFREvent.target.result; //data就是data规范的base64数据
image.src = data;
};
fileReader.readAsDataURL(file);
三. FormData
XMLHttpRequest Level 2添加了一个新的接口FormData.利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()方法来异步的提交这个”表单”.比起普通的ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件.
2.1 FormData构造函数
new FormData (form? : HTMLFormElement)
form(可选):一个HTML表单元素,可以包含任何形式的表单控件,包括文件输入框。
2.2 append()
void append(DOMString name, Blob value, optional DOMString filename);
void append(DOMString name, DOMString value);
给当前FormData对象添加一个键/值对,值可以是Blob和DOMString。
2.3 上传文件
下面是使用FormData的例子:
var oMyForm = new FormData();
oMyForm.append("username", "Groucho");
oMyForm.append("accountnum", 123456); // 数字123456被立即转换成字符串"123456"
// fileInputElement中已经包含了用户所选择的文件
oMyForm.append("userfile", fileInputElement.files[0]);
var oFileBody = 'hey!'; // Blob对象包含的文件内容
var oBlob = new Blob([oFileBody], { type: "text/xml"});
oMyForm.append("webmasterfile", oBlob);
var oReq = new XMLHttpRequest();
oReq.open("POST", "http://foo.com/submitform.php");
oReq.send(oMyForm);
给我的感觉,用FormData就像我们提交form表单,而且还是异步的!有了FormData完全不必要去伪造一个隐藏的form实现文件的上传。ajax默认的content-type是application/x-www-form-urlencoded,使用FormData之后,content-type是multipart/form-data。
用FormData可以非常简单的实现文件上传。并且FormData兼容性也不错:
2.4 伪造上传按钮
自从跟设计师配合之后,选择文件要么被设计成一个icon,要么是一个按钮的样式,美美的。以前,我会放置一个隐藏的input,然后用控制div盖住input,点击div时,相当于点击了input触发文件选择的效果。但是效果不太好,点击域和手势效果不友好。
从Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)开始,你可以隐藏掉默认的的文件输入框 元素,使用自定义的界面来充当打开文件选择对话框的按钮。实现起来很简单,你只需要使用样式display:none把原本的文件输入框隐藏掉,然后在需要的时候调用它的click()方法就行了。
考虑下面的HTML:
Select some files
var fileSelect = document.getElementById("fileSelect"),
fileElem = document.getElementById("fileElem");
fileSelect.addEventListener("click", function (e) {
if (fileElem) {
fileElem.click();
}
e.preventDefault();
}, false);
这样,你就能任意改变这个文件选择按钮的样式了。设计师,来吧,随便虐