本文轉(zhuǎn)載自博客園博主小火柴的藍(lán)色理想绽诚。
Blob
Blob是計算機(jī)界通用術(shù)語之一件甥,全稱寫作:BLOB(binary large object)
藏研,表示二進(jìn)制大對象漠嵌。MySql/Oracle數(shù)據(jù)庫中,就有一種Blob類型盖呼,專門存放二進(jìn)制數(shù)據(jù)儒鹿。在JS中,Blob通常表示二進(jìn)制數(shù)據(jù)几晤,不過它們不一定非得是大量數(shù)據(jù)约炎,Blob也可以表示一個小型文本文件的內(nèi)容。
構(gòu)造函數(shù) Blob(array[, options])
Blob()
構(gòu)造函數(shù)返回一個新的Blob對象蟹瘾,blob的內(nèi)容由參數(shù)數(shù)組中給出的值的串聯(lián)組成圾浅。
參數(shù)array是一個由ArrayBuffer
、ArrayBufferView
憾朴、Blob
狸捕、DOMString
等對象構(gòu)成的Array,或者其他類似對象的混合體众雷,它將會被放進(jìn)Blob灸拍。
參數(shù)options
是一個可選項,它可能會指定如下兩種屬性:
1砾省、類型鸡岗,默認(rèn)值為"",它代表了將會被放入到blob中的數(shù)組內(nèi)容的MIME類型编兄。
2轩性、結(jié)束符,默認(rèn)值為"transparent"
狠鸳,它代表包含行結(jié)束符\n的字符串如何被輸出揣苏。它是以下兩個值中的一個:"native"
,代表行結(jié)束符會被更改為適合宿主操作系統(tǒng)文件系統(tǒng)的慣例碰煌,或者"transparent"舒岸,代表會保持blob中保存的結(jié)束符不變。
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是不透明的芦圾,能對它們進(jìn)行直接操作的就只有獲取它們的大小(以字節(jié)為單位)蛾派、MIME類型以及將它們分割成更小的Blob。
Blob.size(只讀)
:返回Blob對象中所包含數(shù)據(jù)的大小(字節(jié))。
Blob.type(只讀)
:一個字符串洪乍,表明該Blob對象所包含數(shù)據(jù)的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對象中指定范圍內(nèi)的數(shù)據(jù)。
var subblob = blob.slice(0,1024, "text/plain");//Blob中前1KB視為文本
var last = blob.slice(blob.size-1024, 1024);//Blob中最后1KB視為無類型
Web瀏覽器可以將Blob存儲到內(nèi)存中或者磁盤上巷波,Blob可以表示非常大的數(shù)據(jù)塊(比如視頻文件)萎津,如果事先不用slice()方法將它們分割成為小數(shù)據(jù)塊的話,無法存儲在主內(nèi)存中抹镊。正是因為Blob可以表示非常大的數(shù)據(jù)塊锉屈,并且它可能需要磁盤的訪問權(quán)限,所以使用它們的API是異步的(在Worker線程中有提供相應(yīng)的同步版本)垮耳。
文件作為Blob
在使用Blob
之前颈渊,首先必須要獲取Blob。其中一種方式就是把文件作為Blob终佛。
在支持本地文件訪問的瀏覽器中俊嗽,<input type="file">
元素上的files屬性則是一個FileList對象。該對象是一個類數(shù)組對象铃彰,其元素要么是0绍豁,要么是用戶選擇的多個File對象。一個File對象就是一個Blob牙捉,除此之外妹田,還多了name和lastModifiedDate屬性。
<script>
//輸出選中的文件列表相關(guān)的信息
function fileinfo(files) {
for(var i = 0; i < files.length; i++) {//files是一個類數(shù)組對象
var f = files[i];
//a.txt 86 text/plain Mon Sep 19 2016 11:07:43 GMT+0800 (中國標(biāo)準(zhǔn)時間)
console.log(f.name, //只是名字:沒有路徑
f.size, f.type, //size和type是Blob的屬性
f.lastModifiedDate); //修改時間
}
}
</script>
<input type="file" onchange="fileinfo(this.files)"/>
下載Blob
在實際Web應(yīng)用中鹃共,Blob更多是圖片二進(jìn)制形式的上傳與下載鬼佣,雖然其可以實現(xiàn)幾乎任意文件的二進(jìn)制傳輸。
第二種獲取Blob的形式是通過XHR下載Blob霜浴。
var xhr = new XMLHttpRequest(); //創(chuàng)建一個新的XHR對象
xhr.open('GET','p5.gif'); //指定要獲取內(nèi)容的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); //發(fā)送請求
Blob URL
前面介紹了如何獲取或者創(chuàng)建Blob晶衷。下面來介紹如何對獲取或者創(chuàng)建的Blob進(jìn)行操作。其中最簡單的就是可以創(chuàng)建一個URL來指向該Blob阴孟。隨后晌纫,可以以一般的URL形式在任何地方使用該URL:在D0M中、在樣式表中永丝、甚至可以作為XMLHttpRequest的目標(biāo)锹漱。
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
會對內(nèi)容進(jìn)行編碼慕嚷。blob://URL
只是對瀏覽器存儲在內(nèi)存中或者磁盤上的Blob的一個簡單引用哥牍。
file URL
blob://URL
和file://URL
也是不同的毕泌,file://URL
直接指向本地文件系統(tǒng)中的一個文件,僅暴露了文件的路徑嗅辣、瀏覽目錄的許可等撼泛,除此之外任何內(nèi)容都會帶來安全問題的。
Blob URL和創(chuàng)建它們的腳本擁有同樣的源澡谭。這使得它們比file://URL
更加靈活愿题,由于file://URL
是非同源的,因此要在Web應(yīng)用中使用它們相對比較麻煩蛙奖。Blob://URL
只有在同源的文檔中才是有效的潘酗。比如,如果將一個Blob URL通過postMessage()
傳遞給一個非同源窗口雁仲,則該URL對于該窗口來說是沒有任何意義的崎脉。
Blob URL并不是永久有效的。一旦用戶關(guān)閉了或者離開了包含創(chuàng)建Blob URL腳本的文檔伯顶,該Blob URL就失效了。比如骆膝,將Blob URL保存到本地存儲器中祭衩,然后當(dāng)用戶開始一個新的Web應(yīng)用會話的時再使用它,是不可能的阅签。
URL.revokeObjectURL()
URL.createObjectURL()
創(chuàng)建的已經(jīng)存在的URL對象掐暮。當(dāng)結(jié)束使用某個URL對象時,應(yīng)該通過調(diào)用這個方法來讓瀏覽器知道不再需要保持這個文件的引用了政钟。
window.URL.revokeObjectURL(objectURL);
參數(shù)objectURL是一個DOMString路克,表示通過調(diào)用URL.createObjectURL()
方法產(chǎn)生的URL對象。
之所以提供這樣的方式养交,是因為這和內(nèi)存管理問題有關(guān)精算。一旦使用之后,Blob就不再需要了碎连,應(yīng)當(dāng)回收它灰羽。
文件File
不能直接訪問用戶計算機(jī)中的文件,一直都是Web應(yīng)用開發(fā)中的一大障礙鱼辙。2000年以前廉嚼,處理文件的唯一方式就是在表單中加入<input type="file">字段,僅此而已倒戏。FileAPI(文件API)的宗旨是為Web開發(fā)人員提供一種安全的方式怠噪,以便在客戶端訪問用戶計算機(jī)中的文件,并更好地對這些文件執(zhí)行操作杜跷。
File API在表單中的文件輸入字段的基礎(chǔ)上傍念,又添加了一些直接訪問文件信息的接口矫夷。HTML5在DOM中為文件輸入元素添加了一個files集合。在通過文件輸入字段選擇了一或多個文件時捂寿,files集合中將包含一組File對象口四,每個File對象對應(yīng)著一個文件。每個File對象都有下列只讀屬性秦陋。
name:本地文件系統(tǒng)中的文件名
size:文件的字節(jié)大小
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 + '(字節(jié))<br>名稱:' + data.name + '<br>修改時間:' + data.lastModifiedDate;
}
</script>
隱藏文件input
現(xiàn)代瀏覽器支持隱藏掉默認(rèn)的的文件輸入框<input>元素,使用自定義的界面來充當(dāng)打開文件選擇對話框的按鈕顺又。實現(xiàn)起來很簡單更卒,只需要使用樣式display:none
把原本的文件輸入框隱藏掉,然后在需要的時候調(diào)用它的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類型甚至還可以讀取文件中的數(shù)據(jù)。
構(gòu)造函數(shù)
使用FileReader()構(gòu)造函數(shù)來創(chuàng)建一個FileReader對象
var reader = new FileReader();
屬性
error
:表示在讀取文件時發(fā)生的錯誤(只讀)
readyState
:表明FileReader對象的當(dāng)前狀態(tài)果录,值為狀態(tài)常量中的一個(只讀)
狀態(tài)常量有以下三個
常量名 值 描述
EMPTY 0 還沒有加載任何數(shù)據(jù)
LOADING 1 數(shù)據(jù)正在被加載
DONE 2 已完成全部的讀取請求
result
:表示讀取到的文件內(nèi)容上枕,這個屬性只在讀取操作完成之后才有效,并且數(shù)據(jù)的格式取決于讀取操作是由哪個方法發(fā)起的(只讀)弱恒。
方法
FileReader類型實現(xiàn)的是一種異步文件讀取機(jī)制辨萍。可以把FileReader想象成XMLHttpRequest返弹,區(qū)別只是它讀取的是文件系統(tǒng)锈玉,而不是遠(yuǎn)程服務(wù)器。為了讀取文件中的數(shù)據(jù)义起,F(xiàn)ileReader提供了如下幾個方法拉背。
abort()
:中止該讀取操作。
readAsText(file或Blob,encoding)
:以純文本形式讀取File或Blob對象的內(nèi)容默终,將讀取到的文本保存在result屬性中去团。第二個參數(shù)(可選)用于指定編碼類型,默認(rèn)為UTF-8穷蛹。
readAsDataURL(file或Blob)
:讀取File或Blob對象的內(nèi)容土陪,并將文件以數(shù)據(jù)URI(進(jìn)行Base64編碼)的形式保存在result屬性中。
readAsBinaryString(file或Blob)
:讀取File或Blob對象的內(nèi)容肴熏,并將一個字符串保存在result屬性中鬼雀,字符串中的每個字符表示一字節(jié)。
readAsArrayBuffer(file或Blob)
:讀取File或Blob對象的內(nèi)容蛙吏,并將一個包含文件內(nèi)容的ArrayBuffer保存在result屬性中源哩。
這些讀取文件的方法為靈活地處理文件數(shù)據(jù)提供了極大便利鞋吉。例如,可以讀取圖像文件并將其保存為數(shù)據(jù)URI励烦,以便將其顯示給用戶谓着,或者為了解析方便,可以將文件讀取為文本形式坛掠。
事件
由于讀取過程是異步的赊锚,因此FileReader也提供了幾個事件
onabort
:當(dāng)讀取操作被中止時調(diào)用
onerror
:當(dāng)讀取操作發(fā)生錯誤時調(diào)用
onload
:當(dāng)讀取操作成功完成時調(diào)用
onloadend
:當(dāng)讀取操作完成時調(diào)用,不管是成功還是失敗屉栓。該處理程序在onload
或者onerror
之后調(diào)用
onloadstart
:當(dāng)讀取操作將要開始之前調(diào)用
onprogress
:在讀取數(shù)據(jù)過程中周期性調(diào)用
在正常情況下舷蒲,讀取文件時,首先觸發(fā)loadstart
事件友多,此時的readyState
為1牲平,result
為空。
接著域滥,每過50ms左右纵柿,就會觸發(fā)一次progress
事件,通過事件對象可以獲得與XHR的progress事件相同的信息(屬性):lengthComputable启绰、loaded和total昂儒。另外,盡管可能沒有包含全部數(shù)據(jù)酬土,但每次progress事件中都可以通過FileReader
的result
屬性讀取到文件內(nèi)容,readyState
仍然是1格带。
當(dāng)文件讀取完成時撤缴,觸發(fā)load
事件,此時的readyState
為2叽唱,result
為文件內(nèi)容屈呕;如果發(fā)生了error
事件,就不會發(fā)生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>
在觸發(fā)load
虎眨、error
或abort
事件后,會觸發(fā)另一個事件loadend
镶摘。loadend
事件發(fā)生就意味著已經(jīng)讀取完整個文件嗽桩,或者讀取時發(fā)生了錯誤,或者讀取過程被中斷凄敢。
縮略圖
使用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('_');
//檢測圖片是否已經(jīng)被存儲過
if(dataArr.indexOf(id) > -1){
//將保存過的圖片轉(zhuǎn)移到最下方
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>
讀取文件內(nèi)容
<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>
文件進(jìn)度
<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 = '文件進(jìn)度為:' + e.loaded + '/' + e.total;
}
}
}
</script>
文件上傳
方法一:使用表單提交實現(xiàn)文件上傳
文件上傳最基本的方法是使用HTML表單選擇本地文件進(jìn)行上傳锡足,在form表單中通過<input type="file">
標(biāo)記選擇本地文件似将。并且,必須在<form>元素中將enctype設(shè)置為"multipart/form-data"
簿训,將method設(shè)置為"post"罐氨。
另外臀规,需要在<form>表單中設(shè)置一個hidden類型的input框,其中name值為MAX_FILE_SIZE的隱藏值域栅隐,通過設(shè)置其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實現(xiàn)文件上傳
創(chuàng)建一個FormData()對象塔嬉,通過它調(diào)用append()方法并傳入相應(yīng)的File對象作為參數(shù)。然后约啊,再把FormData對象傳遞給XHR的send()方法邑遏。
<input type="file" name="file1" id="file1">
<div id="result"></div>
<script>
var oFile = document.getElementById('file1');
oFile.onchange = function(e){
//創(chuàng)建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])
//異步接受響應(yīng)
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
//實際操作
result.innerHTML = xhr.responseText;
}
}
}
//發(fā)送請求
xhr.open('post','pp.php',true);
xhr.send(data);
}
</script>
方法三:使用File API實現(xiàn)文件上傳
<input type="file" name="file1" id="file1">
<div id="result"></div>
<script>
var oFile = document.getElementById('file1');
oFile.onchange = function(e){
//創(chuàng)建xhr對象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
};
var data = oFile.files[0];
//異步接受響應(yīng)
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
//實際操作
result.innerHTML = xhr.responseText;
}
}
}
//發(fā)送請求
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>