[TOC]
歷史上,JavaScript無法處理二進制數據。如果一定要處理的話,只能使用charCodeAt()方法,一個個字節地從文字編碼轉成二進制數據,還有一種辦法是將二進制數據轉成Base64編碼,再進行處理。這兩種方法不僅速度慢,而且容易出錯。ECMAScript 5引入了Blob對象,允許直接操作二進制數據。
Blob對象是一個代表二進制數據的基本對象,在它的基礎上,又衍生出一系列相關的API,用來操作文件。
* File對象:負責處理那些以文件形式存在的二進制數據,也就是操作本地文件;
* FileList對象:File對象的網頁表單接口;
* FileReader對象:負責將二進制數據讀入內存內容;
* URL對象:用于對二進制數據生成URL。
## Blob對象
Blob(Binary Large Object)對象代表了一段二進制數據,提供了一系列操作接口。其他操作二進制數據的API(比如File對象),都是建立在Blob對象基礎上的,繼承了它的屬性和方法。
生成Blob對象有兩種方法:一種是使用Blob構造函數,另一種是對現有的Blob對象使用slice方法切出一部分。
(1)Blob構造函數,接受兩個參數。第一個參數是一個包含實際數據的數組,第二個參數是數據的類型,這兩個參數都不是必需的。
~~~
var htmlParts = ["<a id=\"a\"><b id=\"b\">hey!<\/b><\/a>"];
var myBlob = new Blob(htmlParts, { "type" : "text\/xml" });
~~~
下面是一個利用Blob對象,生成可下載文件的例子。
~~~
var blob = new Blob(["Hello World"]);
var a = document.createElement("a");
a.href = window.URL.createObjectURL(blob);
a.download = "hello-world.txt";
a.textContent = "Download Hello World!";
body.appendChild(a);
~~~
上面的代碼生成了一個超級鏈接,點擊后提示下載文本文件hello-world.txt,文件內容為“Hello World”。
(2)Blob對象的slice方法,將二進制數據按照字節分塊,返回一個新的Blob對象。
~~~
var newBlob = oldBlob.slice(startingByte, endindByte);
~~~
下面是一個使用XMLHttpRequest對象,將大文件分割上傳的例子。
~~~
function upload(blobOrFile) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/server', true);
xhr.onload = function(e) { ... };
xhr.send(blobOrFile);
}
document.querySelector('input[type="file"]').addEventListener('change', function(e) {
var blob = this.files[0];
const BYTES_PER_CHUNK = 1024 * 1024; // 1MB chunk sizes.
const SIZE = blob.size;
var start = 0;
var end = BYTES_PER_CHUNK;
while(start < SIZE) {
upload(blob.slice(start, end));
start = end;
end = start + BYTES_PER_CHUNK;
}
}, false);
})();
~~~
(3)Blob對象有兩個只讀屬性:
* size:二進制數據的大小,單位為字節。
* type:二進制數據的MIME類型,全部為小寫,如果類型未知,則該值為空字符串。
在Ajax操作中,如果xhr.responseType設為blob,接收的就是二進制數據。
## FileList對象
FileList對象針對表單的file控件。當用戶通過file控件選取文件后,這個控件的files屬性值就是FileList對象。它在結構上類似于數組,包含用戶選取的多個文件。
~~~
<input type="file" id="input" onchange="console.log(this.files.length)" multiple />
~~~
當用戶選取文件后,就可以讀取該文件。
~~~
var selected_file = document.getElementById('input').files[0];
~~~
采用拖放方式,也可以得到FileList對象。
~~~
var dropZone = document.getElementById('drop_zone');
dropZone.addEventListener('drop', handleFileSelect, false);
function handleFileSelect(evt) {
evt.stopPropagation();
evt.preventDefault();
var files = evt.dataTransfer.files; // FileList object.
// ...
}
~~~
上面代碼的 handleFileSelect 是拖放事件的回調函數,它的參數evt是一個事件對象,該參數的dataTransfer.files屬性就是一個FileList對象,里面包含了拖放的文件。
## File對象
File對象是FileList對象的成員,包含了文件的一些元信息,比如文件名、上次改動時間、文件大小和文件類型。它的屬性值如下:
* name:文件名,該屬性只讀。
* size:文件大小,單位為字節,該屬性只讀。
* type:文件的MIME類型,如果分辨不出類型,則為空字符串,該屬性只讀。
* lastModifiedDate:文件的上次修改時間。
~~~
var selected_file = document.getElementById('input').files[0];
var fileName = selected_file.name;
var fileSize = selected_file.size;
var fileType = selected_file.type;
~~~
## FileReader對象
FileReader對象用于讀取文件,即把文件內容讀入內存。它的參數是File對象或Blob對象。
對于不同類型的文件,FileReader使用不同的方法讀取。
* readAsBinaryString(Blob|File):返回二進制字符串,該字符串每個字節包含一個0到255之間的整數。
* readAsText(Blob|File, opt_encoding)?:返回文本字符串。默認情況下,文本編碼格式是'UTF-8',可以通過可選的格式參數,指定其他編碼格式的文本。
* readAsDataURL(Blob|File):返回一個基于Base64編碼的data-uri對象。
* readAsArrayBuffer(Blob|File)?:返回一個ArrayBuffer對象。
readAsText方法用于讀取文本文件,它的第一個參數是File或Blob對象,第二個參數是前一個參數的編碼方法,如果省略就默認為UTF-8編碼。該方法是異步方法,一般監聽onload事件,用來確定文件是否加載結束,方法是判斷FileReader實例的result屬性是否有值。其他三種讀取方法,用法與readAsText方法類似。
~~~
var reader = new FileReader();
reader.onload = function(e) {
var text = reader.result;
}
reader.readAsText(file, encoding);
~~~
readAsDataURL方法返回一個data URL,它的作用基本上是將文件數據進行Base64編碼。你可以將返回值設為圖像的src屬性。
~~~
var reader = new FileReader();
reader.onload = function(e) {
var dataURL = reader.result;
}
reader.readAsDataURL(file);
~~~
readAsBinaryString方法可以讀取任意類型的文件,而不僅僅是文本文件,返回文件的原始的二進制內容。這個方法與XMLHttpRequest.sendAsBinary方法結合使用,就可以使用JavaScript上傳任意文件到服務器。
~~~
var reader = new FileReader();
reader.onload = function(e) {
var rawData = reader.result;
}
reader.readAsBinaryString(file);
~~~
readAsArrayBuffer方法讀取文件,返回一個類型化數組(ArrayBuffer),即固定長度的二進制緩存數據。在文件操作時(比如將JPEG圖像轉為PNG圖像),這個方法非常方便。
~~~
var reader = new FileReader();
reader.onload = function(e) {
var arrayBuffer = reader.result;
}
reader.readAsArrayBuffer(file);
~~~
除了以上四種不同的讀取文件方法,FileReader對象還有一個abort方法,用于中止文件上傳。
~~~
var reader = new FileReader();
reader.abort();
~~~
FileReader對象采用異步方式讀取文件,可以為一系列事件指定回調函數。
* onabort方法:讀取中斷或調用reader.abort()方法時觸發。
* onerror方法:讀取出錯時觸發。
* onload方法:讀取成功后觸發。
* onloadend方法:讀取完成后觸發,不管是否成功。觸發順序排在 onload 或 onerror 后面。
* onloadstart方法:讀取將要開始時觸發。
* onprogress方法:讀取過程中周期性觸發。
下面的代碼是如何展示文本文件的內容。
~~~
var reader = new FileReader();
reader.onload = function(e){
console.log(e.target.result);
}
reader.readAsText(blob);
~~~
onload事件的回調函數接受一個事件對象,該對象的target.result就是文件的內容。
下面是一個使用readAsDataURL方法,為img元素添加src屬性的例子。
~~~
var reader = new FileReader();
reader.onload = function(e) {
document.createElement('img').src = e.target.result;
};
reader.readAsDataURL(f);
~~~
下面是一個onerror事件回調函數的例子。
~~~
var reader = new FileReader();
reader.onerror = errorHandler;
function errorHandler(evt) {
switch(evt.target.error.code) {
case evt.target.error.NOT_FOUND_ERR:
alert('File Not Found!');
break;
case evt.target.error.NOT_READABLE_ERR:
alert('File is not readable');
break;
case evt.target.error.ABORT_ERR:
break;
default:
alert('An error occurred reading this file.');
};
}
~~~
下面是一個onprogress事件回調函數的例子,主要用來顯示讀取進度。
~~~
var reader = new FileReader();
reader.onprogress = updateProgress;
function updateProgress(evt) {
if (evt.lengthComputable) {
var percentLoaded = Math.round((evt.loaded / evt.totalEric Bidelman) * 100);
var progress = document.querySelector('.percent');
if (percentLoaded < 100) {
progress.style.width = percentLoaded + '%';
progress.textContent = percentLoaded + '%';
}
}
}
~~~
讀取大文件的時候,可以利用Blob對象的slice方法,將大文件分成小段,逐一讀取,這樣可以加快處理速度。
## 綜合實例:顯示用戶選取的本地圖片
假設有一個表單,用于用戶選取圖片。
~~~
<input type="file" name="picture" accept="image/png, image/jpeg"/>
~~~
一旦用戶選中圖片,將其顯示在canvas的函數可以這樣寫:
~~~
document.querySelector('input[name=picture]').onchange = function(e){
readFile(e.target.files[0]);
}
function readFile(file){
var reader = new FileReader();
reader.onload = function(e){
applyDataUrlToCanvas( reader.result );
};
reader.reaAsDataURL(file);
}
~~~
還可以在canvas上面定義拖放事件,允許用戶直接拖放圖片到上面。
~~~
// stop FireFox from replacing the whole page with the file.
canvas.ondragover = function () { return false; };
// Add drop handler
canvas.ondrop = function (e) {
e.stopPropagation();
e.preventDefault();
e = e || window.event;
var files = e.dataTransfer.files;
if(files){
readFile(files[0]);
}
};
~~~
所有的拖放事件都有一個dataTransfer屬性,它包含拖放過程涉及的二進制數據。
還可以讓canvas顯示剪貼板中的圖片。
~~~
document.onpaste = function(e){
e.preventDefault();
if(e.clipboardData&&e.clipboardData.items){
// pasted image
for(var i=0, items = e.clipboardData.items;i<items.length;i++){
if( items[i].kind==='file' && items[i].type.match(/^image/) ){
readFile(items[i].getAsFile());
break;
}
}
}
return false;
};
~~~
## URL對象
URL對象用于生成指向File對象或Blob對象的URL。
~~~
var objecturl = window.URL.createObjectURL(blob);
~~~
上面的代碼會對二進制數據生成一個URL,類似于“blob:http%3A//test.com/666e6730-f45c-47c1-8012-ccc706f17191”。這個URL可以放置于任何通常可以放置URL的地方,比如img標簽的src屬性。需要注意的是,即使是同樣的二進制數據,每調用一次URL.createObjectURL方法,就會得到一個不一樣的URL。
這個URL的存在時間,等同于網頁的存在時間,一旦網頁刷新或卸載,這個URL就失效。除此之外,也可以手動調用URL.revokeObjectURL方法,使URL失效。
~~~
window.URL.revokeObjectURL(objectURL);
~~~
下面是一個利用URL對象,在網頁插入圖片的例子。
~~~
var img = document.createElement("img");
img.src = window.URL.createObjectURL(files[0]);
img.height = 60;
img.onload = function(e) {
window.URL.revokeObjectURL(this.src);
}
body.appendChild(img);
var info = document.createElement("span");
info.innerHTML = files[i].name + ": " + files[i].size + " bytes";
body.appendChild(info);
~~~
還有一個本機視頻預覽的例子。
~~~
var video = document.getElementById('video');
var obj_url = window.URL.createObjectURL(blob);
video.src = obj_url;
video.play()
window.URL.revokeObjectURL(obj_url);
~~~
## 參考鏈接
* [W3C Working Draft](http://www.w3.org/TR/FileAPI/)
* Andrew Dodson,?[Get Loaded with the File API](http://msdn.microsoft.com/en-gb/magazine/jj835793.aspx)
* Mozilla Developer Network,[Using files from web applications](https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications)
* [HTML5 download attribute](http://javascript-reverse.tumblr.com/post/37056936789/html5-download-attribute)
* Eric Bidelman,?[Reading files in JavaScript using the File APIs](http://www.html5rocks.com/en/tutorials/file/dndfiles/)
* Matt West,?[Reading Files Using The HTML5 FileReader API](http://blog.teamtreehouse.com/reading-files-using-the-html5-filereader-api)
- 第一章 導論
- 1.1 前言
- 1.2 為什么學習JavaScript?
- 1.3 JavaScript的歷史
- 第二章 基本語法
- 2.1 語法概述
- 2.2 數值
- 2.3 字符串
- 2.4 對象
- 2.5 數組
- 2.6 函數
- 2.7 運算符
- 2.8 數據類型轉換
- 2.9 錯誤處理機制
- 2.10 JavaScript 編程風格
- 第三章 標準庫
- 3.1 Object對象
- 3.2 Array 對象
- 3.3 包裝對象和Boolean對象
- 3.4 Number對象
- 3.5 String對象
- 3.6 Math對象
- 3.7 Date對象
- 3.8 RegExp對象
- 3.9 JSON對象
- 3.10 ArrayBuffer:類型化數組
- 第四章 面向對象編程
- 4.1 概述
- 4.2 封裝
- 4.3 繼承
- 4.4 模塊化編程
- 第五章 DOM
- 5.1 Node節點
- 5.2 document節點
- 5.3 Element對象
- 5.4 Text節點和DocumentFragment節點
- 5.5 Event對象
- 5.6 CSS操作
- 5.7 Mutation Observer
- 第六章 瀏覽器對象
- 6.1 瀏覽器的JavaScript引擎
- 6.2 定時器
- 6.3 window對象
- 6.4 history對象
- 6.5 Ajax
- 6.6 同域限制和window.postMessage方法
- 6.7 Web Storage:瀏覽器端數據儲存機制
- 6.8 IndexedDB:瀏覽器端數據庫
- 6.9 Web Notifications API
- 6.10 Performance API
- 6.11 移動設備API
- 第七章 HTML網頁的API
- 7.1 HTML網頁元素
- 7.2 Canvas API
- 7.3 SVG 圖像
- 7.4 表單
- 7.5 文件和二進制數據的操作
- 7.6 Web Worker
- 7.7 SSE:服務器發送事件
- 7.8 Page Visibility API
- 7.9 Fullscreen API:全屏操作
- 7.10 Web Speech
- 7.11 requestAnimationFrame
- 7.12 WebSocket
- 7.13 WebRTC
- 7.14 Web Components
- 第八章 開發工具
- 8.1 console對象
- 8.2 PhantomJS
- 8.3 Bower:客戶端庫管理工具
- 8.4 Grunt:任務自動管理工具
- 8.5 Gulp:任務自動管理工具
- 8.6 Browserify:瀏覽器加載Node.js模塊
- 8.7 RequireJS和AMD規范
- 8.8 Source Map
- 8.9 JavaScript 程序測試
- 第九章 JavaScript高級語法
- 9.1 Promise對象
- 9.2 有限狀態機
- 9.3 MVC框架與Backbone.js
- 9.4 嚴格模式
- 9.5 ECMAScript 6 介紹
- 附錄
- 10.1 JavaScript API列表
- 草稿一:函數庫
- 11.1 Underscore.js
- 11.2 Modernizr
- 11.3 Datejs
- 11.4 D3.js
- 11.5 設計模式
- 11.6 排序算法
- 草稿二:jQuery
- 12.1 jQuery概述
- 12.2 jQuery工具方法
- 12.3 jQuery插件開發
- 12.4 jQuery.Deferred對象
- 12.5 如何做到 jQuery-free?
- 草稿三:Node.js
- 13.1 Node.js 概述
- 13.2 CommonJS規范
- 13.3 package.json文件
- 13.4 npm模塊管理器
- 13.5 fs 模塊
- 13.6 Path模塊
- 13.7 process對象
- 13.8 Buffer對象
- 13.9 Events模塊
- 13.10 stream接口
- 13.11 Child Process模塊
- 13.12 Http模塊
- 13.13 assert 模塊
- 13.14 Cluster模塊
- 13.15 os模塊
- 13.16 Net模塊和DNS模塊
- 13.17 Express框架
- 13.18 Koa 框架