本文转载自博客园博主小火柴的蓝色理想。
Blob
Blob是计算机界通用术语之一,全称写作:BLOB(binary large object)
,表示二进制大对象。MySql/Oracle数据库中,就有一种Blob类型,专门存放二进制数据。在JS中,Blob通常表示二进制数据,不过它们不一定非得是大量数据,Blob也可以表示一个小型文本文件的内容。
构造函数 Blob(array[, options])
Blob()
构造函数返回一个新的Blob对象,blob的内容由参数数组中给出的值的串联组成。
参数array是一个由ArrayBuffer
、ArrayBufferView
、Blob
、DOMString
等对象构成的Array,或者其他类似对象的混合体,它将会被放进Blob。
参数options
是一个可选项,它可能会指定如下两种属性:
1、类型,默认值为"",它代表了将会被放入到blob中的数组内容的MIME类型。
2、结束符,默认值为"transparent"
,它代表包含行结束符\n的字符串如何被输出。它是以下两个值中的一个:"native"
,代表行结束符会被更改为适合宿主操作系统文件系统的惯例,或者"transparent",代表会保持blob中保存的结束符不变。
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>'];
var oMyBlob = new Blob(aFileParts, {type : 'text/html'});
console.log(oMyBlob);//Blob {size: 32, type: "text/html"}
属性和方法
Blob是不透明的,能对它们进行直接操作的就只有获取它们的大小(以字节为单位)、MIME类型以及将它们分割成更小的Blob。
Blob.size(只读)
:返回Blob对象中所包含数据的大小(字节)。
Blob.type(只读)
:一个字符串,表明该Blob对象所包含数据的MIME类型。如果类型未知,则该值为空字符串。
var myBlob = new Blob([1,2,3],{type:'text/plain'});
console.log(myBlob);//Blob {size: 3, type: "text/plain"}
console.log(myBlob.size);//3
console.log(myBlob.type);//'text/plain'
Blob.slice([start[, end[, contentType]]])
slice()
方法返回一个新的Blob对象,包含了源Blob对象中指定范围内的数据。
var subblob = blob.slice(0,1024, "text/plain");//Blob中前1KB视为文本
var last = blob.slice(blob.size-1024, 1024);//Blob中最后1KB视为无类型
Web浏览器可以将Blob存储到内存中或者磁盘上,Blob可以表示非常大的数据块(比如视频文件),如果事先不用slice()方法将它们分割成为小数据块的话,无法存储在主内存中。正是因为Blob可以表示非常大的数据块,并且它可能需要磁盘的访问权限,所以使用它们的API是异步的(在Worker线程中有提供相应的同步版本)。
文件作为Blob
在使用Blob
之前,首先必须要获取Blob。其中一种方式就是把文件作为Blob。
在支持本地文件访问的浏览器中,<input type="file">
元素上的files属性则是一个FileList对象。该对象是一个类数组对象,其元素要么是0,要么是用户选择的多个File对象。一个File对象就是一个Blob,除此之外,还多了name和lastModifiedDate属性。
<script>
//输出选中的文件列表相关的信息
function fileinfo(files) {
for(var i = 0; i < files.length; i++) {//files是一个类数组对象
var f = files[i];
//a.txt 86 text/plain Mon Sep 19 2016 11:07:43 GMT+0800 (中国标准时间)
console.log(f.name, //只是名字:没有路径
f.size, f.type, //size和type是Blob的属性
f.lastModifiedDate); //修改时间
}
}
</script>
<input type="file" onchange="fileinfo(this.files)"/>
下载Blob
在实际Web应用中,Blob更多是图片二进制形式的上传与下载,虽然其可以实现几乎任意文件的二进制传输。
第二种获取Blob的形式是通过XHR下载Blob。
var xhr = new XMLHttpRequest(); //创建一个新的XHR对象
xhr.open('GET','p5.gif'); //指定要获取内容的URL
xhr.responseType = 'blob'; //以Blob的形式
xhr.onload = function(){ //onload 比onreadystatechange更容易
//Blob {size: 944, type: "image/gif"}
console.log(xhr.response); //response返回的就是Blob对象
}
xhr.send(null); //发送请求
Blob URL
前面介绍了如何获取或者创建Blob。下面来介绍如何对获取或者创建的Blob进行操作。其中最简单的就是可以创建一个URL来指向该Blob。随后,可以以一般的URL形式在任何地方使用该URL:在D0M中、在样式表中、甚至可以作为XMLHttpRequest的目标。
objectURL = URL.createObjectURL(blob);
var xhr = new XMLHttpRequest();
xhr.open('GET','test/p5.gif');
xhr.responseType = 'blob';
xhr.onload = function(){
//blob:http://127.0.0.1/539ae798-70db-44db-b216-fc932b358285
console.log(URL.createObjectURL(xhr.response));
}
xhr.send(null);
[注意]blob://URL
和data://URL
是不同的,data://URL
会对内容进行编码。blob://URL
只是对浏览器存储在内存中或者磁盘上的Blob的一个简单引用。
file URL
blob://URL
和file://URL
也是不同的,file://URL
直接指向本地文件系统中的一个文件,仅暴露了文件的路径、浏览目录的许可等,除此之外任何内容都会带来安全问题的。
Blob URL和创建它们的脚本拥有同样的源。这使得它们比file://URL
更加灵活,由于file://URL
是非同源的,因此要在Web应用中使用它们相对比较麻烦。Blob://URL
只有在同源的文档中才是有效的。比如,如果将一个Blob URL通过postMessage()
传递给一个非同源窗口,则该URL对于该窗口来说是没有任何意义的。
Blob URL并不是永久有效的。一旦用户关闭了或者离开了包含创建Blob URL脚本的文档,该Blob URL就失效了。比如,将Blob URL保存到本地存储器中,然后当用户开始一个新的Web应用会话的时再使用它,是不可能的。
URL.revokeObjectURL()
URL.createObjectURL()
创建的已经存在的URL对象。当结束使用某个URL对象时,应该通过调用这个方法来让浏览器知道不再需要保持这个文件的引用了。
window.URL.revokeObjectURL(objectURL);
参数objectURL是一个DOMString,表示通过调用URL.createObjectURL()
方法产生的URL对象。
之所以提供这样的方式,是因为这和内存管理问题有关。一旦使用之后,Blob就不再需要了,应当回收它。
文件File
不能直接访问用户计算机中的文件,一直都是Web应用开发中的一大障碍。2000年以前,处理文件的唯一方式就是在表单中加入<input type="file">字段,仅此而已。FileAPI(文件API)的宗旨是为Web开发人员提供一种安全的方式,以便在客户端访问用户计算机中的文件,并更好地对这些文件执行操作。
File API在表单中的文件输入字段的基础上,又添加了一些直接访问文件信息的接口。HTML5在DOM中为文件输入元素添加了一个files集合。在通过文件输入字段选择了一或多个文件时,files集合中将包含一组File对象,每个File对象对应着一个文件。每个File对象都有下列只读属性。
name:本地文件系统中的文件名
size:文件的字节大小
type:字符串,文件的MIME类型
lastModifiedDate:字符串,文件上一次被修改的时间
通过侦听change事件并读取files集合就可以知道选择的每个文件的信息。
<input type="file" id="file1">
<div id="result"></div>
<script>
file1.onchange = function(){
var data = file1.files[0];
result.innerHTML = '类型:' + data.type + '<br>大小:' + data.size + '(字节)<br>名称:' + data.name + '<br>修改时间:' + data.lastModifiedDate;
}
</script>
隐藏文件input
现代浏览器支持隐藏掉默认的的文件输入框<input>元素,使用自定义的界面来充当打开文件选择对话框的按钮。实现起来很简单,只需要使用样式display:none
把原本的文件输入框隐藏掉,然后在需要的时候调用它的click()方法就行了。
<input type="file" id="file1" style="display:none">
<button id="btn">按钮</button>
<div id="result"></div>
<script>
btn.onclick = function(){
file1.click();
}
file1.onchange = function(){
result.innerHTML = file1.files[0].name;
}
</script>
FileReader
File API的功能还不止于此,通过它提供的FileReader类型甚至还可以读取文件中的数据。
构造函数
使用FileReader()构造函数来创建一个FileReader对象
var reader = new FileReader();
属性
error
:表示在读取文件时发生的错误(只读)
readyState
:表明FileReader对象的当前状态,值为状态常量中的一个(只读)
状态常量有以下三个
常量名 值 描述
EMPTY 0 还没有加载任何数据
LOADING 1 数据正在被加载
DONE 2 已完成全部的读取请求
result
:表示读取到的文件内容,这个属性只在读取操作完成之后才有效,并且数据的格式取决于读取操作是由哪个方法发起的(只读)。
方法
FileReader类型实现的是一种异步文件读取机制。可以把FileReader想象成XMLHttpRequest,区别只是它读取的是文件系统,而不是远程服务器。为了读取文件中的数据,FileReader提供了如下几个方法。
abort()
:中止该读取操作。
readAsText(file或Blob,encoding)
:以纯文本形式读取File或Blob对象的内容,将读取到的文本保存在result属性中。第二个参数(可选)用于指定编码类型,默认为UTF-8。
readAsDataURL(file或Blob)
:读取File或Blob对象的内容,并将文件以数据URI(进行Base64编码)的形式保存在result属性中。
readAsBinaryString(file或Blob)
:读取File或Blob对象的内容,并将一个字符串保存在result属性中,字符串中的每个字符表示一字节。
readAsArrayBuffer(file或Blob)
:读取File或Blob对象的内容,并将一个包含文件内容的ArrayBuffer保存在result属性中。
这些读取文件的方法为灵活地处理文件数据提供了极大便利。例如,可以读取图像文件并将其保存为数据URI,以便将其显示给用户,或者为了解析方便,可以将文件读取为文本形式。
事件
由于读取过程是异步的,因此FileReader也提供了几个事件
onabort
:当读取操作被中止时调用
onerror
:当读取操作发生错误时调用
onload
:当读取操作成功完成时调用
onloadend
:当读取操作完成时调用,不管是成功还是失败。该处理程序在onload
或者onerror
之后调用
onloadstart
:当读取操作将要开始之前调用
onprogress
:在读取数据过程中周期性调用
在正常情况下,读取文件时,首先触发loadstart
事件,此时的readyState
为1,result
为空。
接着,每过50ms左右,就会触发一次progress
事件,通过事件对象可以获得与XHR的progress事件相同的信息(属性):lengthComputable、loaded和total。另外,尽管可能没有包含全部数据,但每次progress事件中都可以通过FileReader
的result
属性读取到文件内容,readyState
仍然是1。
当文件读取完成时,触发load
事件,此时的readyState
为2,result
为文件内容;如果发生了error
事件,就不会发生load
事件。
<input type="file" id="file1">
<div id="result"></div>
<script>
/*
[loadstart]readyState:1;result:
[progress]readyState:1;result:h3{color: #F44336;}
[load]readyState:2;result:h3{color: #F44336;}
[loadend]readyState:2;result:h3{color: #F44336;}
*/
file1.onchange = function(){
var reader = new FileReader();
reader.readAsText(file1.files[0]);
reader.onloadstart = function(e){
console.log('[loadstart]readyState:' + reader.readyState + ';result:' + reader.result);
}
reader.onload = function(e){
console.log('[load]readyState:' + reader.readyState + ';result:' + reader.result);
}
reader.onloadend = function(e){
console.log('[loadend]readyState:' + reader.readyState + ';result:' + reader.result);
}
reader.onprogress = function(e){
console.log('[progress]readyState:' + reader.readyState + ';result:' + reader.result);
}
}
</script>
在触发load
、error
或abort
事件后,会触发另一个事件loadend
。loadend
事件发生就意味着已经读取完整个文件,或者读取时发生了错误,或者读取过程被中断。
缩略图
使用FileReader
对象的readAsDataURL()
方法完成对文件的读取,再通过File对象的type属性筛选出图片。
<img id="uploadPreview" style="width: 100px; height: 100px;" src="data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3Csvg%20width%3D%22153%22%20height%3D%22153%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%0A%20%3Cg%3E%0A%20%20%3Ctitle%3ENo%20image%3C/title%3E%0A%20%20%3Crect%20id%3D%22externRect%22%20height%3D%22150%22%20width%3D%22150%22%20y%3D%221.5%22%20x%3D%221.500024%22%20stroke-width%3D%223%22%20stroke%3D%22%23666666%22%20fill%3D%22%23e1e1e1%22/%3E%0A%20%20%3Ctext%20transform%3D%22matrix%286.66667%2C%200%2C%200%2C%206.66667%2C%20-960.5%2C%20-1099.33%29%22%20xml%3Aspace%3D%22preserve%22%20text-anchor%3D%22middle%22%20font-family%3D%22Fantasy%22%20font-size%3D%2214%22%20id%3D%22questionMark%22%20y%3D%22181.249569%22%20x%3D%22155.549819%22%20stroke-width%3D%220%22%20stroke%3D%22%23666666%22%20fill%3D%22%23000000%22%3E%3F%3C/text%3E%0A%20%3C/g%3E%0A%3C/svg%3E" alt="Image preview" />
<input type="file" id="file1" style="display:none">
<button id="btn">选择图片</button>
<span id="msgName"></span>
<script>
btn.onclick = function(){
file1.click();
}
file1.onchange = function(){
var file = file1.files[0];
//如果一个文件被选中
if(file){
//一张图片被选中
if (/image/.test(file.type)){
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(){
uploadPreview.src = reader.result;
msgName.innerHTML = file.name;
}
//其他格式文件被选中
} else {
alert("You must select a valid image file!");
}
}
}
</script>
使用Blob也能显示缩略图
<input id="file1" type="file" accept="image/gif,image/jpeg,image/jpg,image/png,image/x-icon" style="display:none">
<button id="btn">选择图片</button>
<div id="fileList"></div>
<script>
btn.onclick = function(){file1.click();}
//保存图片的名称
var dataArr = [];
file1.onchange = function(){
var file = file1.files[0];
//如果一个文件被选中
if(file){
var name = file.name;
var id = name.split('.').join('_');
//检测图片是否已经被存储过
if(dataArr.indexOf(id) > -1){
//将保存过的图片转移到最下方
fileList.appendChild(document.getElementById(id));
}else{
if(/image/.test(file.type)){
dataArr.push(id);
var img = document.createElement('img');
img.onload = function(){
URL.revokeObjectURL(img.src);
}
img.src= URL.createObjectURL(file);
img.height = 60;
var oDiv = document.createElement('div');
oDiv.id = id;
var oSpan = document.createElement('span');
oSpan.innerHTML = name + ":" + file.size + " bytes";
oDiv.appendChild(img);
oDiv.appendChild(oSpan);
fileList.appendChild(oDiv);
}
}
}
}
</script>
读取文件内容
<input id="file1" type="file" style="display:none">
<button id="btn">选择文件</button>
<div id="fileData" style="border:1px solid black;width:300px;overflow:auto"></div>
<script>
btn.onclick = function(){file1.click();}
file1.onchange = function(){
var file = file1.files[0];
//如果一个文件被选中
if(file){
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(){
fileData.innerHTML = reader.result;
}
}
}
</script>
文件进度
<input id="file1" type="file" style="display:none">
<button id="btn">选择文件</button>
<div id="fileData"></div>
<script>
btn.onclick = function(){file1.click();}
file1.onchange = function(){
var file = file1.files[0];
//如果一个文件被选中
if(file){
var reader = new FileReader();
reader.readAsText(file);
reader.onprogress = function(e){
e = e || event;
fileData.innerHTML = '文件进度为:' + e.loaded + '/' + e.total;
}
}
}
</script>
文件上传
方法一:使用表单提交实现文件上传
文件上传最基本的方法是使用HTML表单选择本地文件进行上传,在form表单中通过<input type="file">
标记选择本地文件。并且,必须在<form>元素中将enctype设置为"multipart/form-data"
,将method设置为"post"。
另外,需要在<form>表单中设置一个hidden类型的input框,其中name值为MAX_FILE_SIZE的隐藏值域,通过设置其value值限制上传文件的大小
<form action="pp.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000">
<input type="file" name="file1">
<button>上传文件</button>
</form>
方法二:使用FormData实现文件上传
创建一个FormData()对象,通过它调用append()方法并传入相应的File对象作为参数。然后,再把FormData对象传递给XHR的send()方法。
<input type="file" name="file1" id="file1">
<div id="result"></div>
<script>
var oFile = document.getElementById('file1');
oFile.onchange = function(e){
//创建xhr对象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
};
var data = new FormData();
data.append('file',oFile.files[0])
//异步接受响应
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
//实际操作
result.innerHTML = xhr.responseText;
}
}
}
//发送请求
xhr.open('post','pp.php',true);
xhr.send(data);
}
</script>
方法三:使用File API实现文件上传
<input type="file" name="file1" id="file1">
<div id="result"></div>
<script>
var oFile = document.getElementById('file1');
oFile.onchange = function(e){
//创建xhr对象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
};
var data = oFile.files[0];
//异步接受响应
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
//实际操作
result.innerHTML = xhr.responseText;
}
}
}
//发送请求
xhr.open('post','pp.php',true);
xhr.setRequestHeader("content-type",data.type);
xhr.send(data);
}
</script>
删除文件
使用<input type="file>控件选择文件后,如何删除文件呢?一般地,有两种方法。一种是使用form表单的reset()方法,重置表单;另一种是将<input type="file>控件的value值置空,但第二种方法IE10-浏览器不支持。
<form id="myForm">
<input type="file" id="myFile">
</form>
<button id="btn1">删除文件方法1</button>
<button id="btn2">删除文件方法2</button>
<script>
var myFile = document.getElementById('myFile');
var myForm = document.getElementById('myForm');
btn1.onclick = function(){
myFile.value = '';
}
btn2.onclick = function(){
myForm.reset();
}
</script>