[WebAPIs] Blob, File 和 FileReader
keywords: file
, blob
, ArrayBuffer
, FIleList
, FileReader
- Blob @ MDN - Web APIs
- FileReader @ MDN - Web APIs
- File 物件,FileList 物件,FileReader 物件 @ 阮一峰
關聯文章:
- [TypedArray, ArrayBuffer, DataView](/Users/pjchen/Projects/Notes/source/_posts/JavaScript/[JS] TypedArray, ArrayBuffer 和 DataView.md)
- [檔案上傳(Input File) and File Upload](/Users/pjchen/Projects/Notes/source/_posts/WebAPIs/[WEB] 檔案上傳 Input File and File Upload.md)
- [Blob and File API](/Users/pjchen/Projects/Notes/source/_posts/WebAPIs/[WEB] Blob and File API.md)
Blob 概念
keywords: new Blob()
, Blob.prototype.size
, Blob.prototype.type
, Blob.prototype.slice()
Blob 是 Binary Large Object 的縮寫,表示的是二進位檔案的資料內容,透過 Blob,JavaScript 才能讀寫二進位資料的檔案。和 ArrayBuffer 不同的地方是,ArrayBuffer 是操作記憶體,而 Blob 是用來操作二進位檔案(但尚未參照到實體檔案上)。
Blob 物件指的是能夠用來表徵檔案,帶有許多資料位元的 chunks,但它實際上並沒有參照到真正的檔案。Blob 物件和檔案一樣,它有自己的檔案大小和 MIME 格式,它會根據瀏覽器或 blob size 儲存在記憶體或檔案系統中。使用上,它就和使用檔案一樣。
Blob 的內容可以轉換成 ArrayBuffer ,因此可以輕易的把它存成二元資料
- 想從 non-blob 物件或資料建立一個 Blob 可以使用
Blob()
建構式。 - 想從已經包含 blob 的資料中建立 Blob 可以使用
slice()
方法。 - 想從使用者系統中取得檔案的
Blob
物件,需要參考File
文件。
建立 Blob 物件
透過 Blob()
建構式,可以從其他物 件建立出 Blob 物件,Blob 物件會包含 type
和 size
兩者屬性可以使用:
// new Blob(array [, options])
// - array 是字元串或二進位物件(ArrayBuffer, ArrayBufferView, Blob, DOMString)
// - options 用來指定 MIME Type
var htmlFragment = '<div><h1>Hello Blob</h1></div>';
var htmlBlob = new Blob([htmlFragment], { type: 'text/html' });
// 常用的檔案格式
var jsonData = JSON.stringify({ hello: 'world' }, null, 2);
new Blob([jsonData], { type: 'application/json' });
var cssFormat = 'body { background-color: yellow; }';
new Blob([cssFormat], { type: 'text/css' });
array 可以是由
ArrayBuffer
,ArrayBufferView
,Blob
,DOMString
這些物件組成的陣列。
範例
var debug = { hello: 'world' };
var blob = new Blob([JSON.stringify(debug, null, 2)], {
type: 'application/json',
});
console.log(blob); // Blob(22) {size: 22, type: "application/json"}
Blob() 建構式 @ MDN - Web APIs
複製 Blob (Blob.prototype.slice)
透過 Blob 的 slice
方法可以複製 Blob:
blob.slice(startByte, endByte, contentType);
讀取 Blob(FileReader)
- FileReader @ MDN
- FileReader 物件 @ 阮一峰
透過 File
和 Blob
物件可以指定讓網頁讀取的檔案,接著透過 FileReader 物件可以讓網頁以非同步的方式讀取存在電腦的實體檔案內容。
File 物件的取得可以靠
<input type="file">
或拖拉的DataTransfer
物件等方式取得。
要讀取 Blob 內容的唯一方式是透過 FileReader
,透過 FileReader 可以將 Blob 轉換成 TypedArray:
/**
* extracting data from a Blob
* https://developer.mozilla.org/en-US/docs/Web/API/Blob
* 透過 readAsArrayBuffer 可以將 blob 讀取出來成為 typed Array
* FileReader.readyState
* - 0 表示尚未載入任何資料
* - 1 表示資料正在載入
* - 2 表示載入完成
**/
var reader = new FileReader();
reader.addEventListener('load', function () {
console.log(reader.result);
// reader.result contains the contents of blob as a typed array
});
reader.readAsArrayBuffer(blob);
方法(Methods)
下述方法可以將 File / Blob 物件轉換成 TypedArray
FileReader.readAsArrayBuffer()
:以 TypedArray 的方式顯示檔案內容。FileReader.readAsDataURL()
:以data:<MIME Type>;base64,...
的方式顯示檔案內容,讀取的資料 是圖檔的話會轉成 Base64,**可以直接用於<img src="BASE_64">
中;讀取的資料是 CSS 等若無法直接辨識的檔案類型,會轉成application/octet-stream
的 Base 64 檔案。FileReader.readAsText()
:以純文字的方式顯示檔案內容,預設是 UTF-8。FileReader.readAsBinaryString()
:以原始二進制顯示檔案內容。
⚠️ 這個 DataURL 取得的字串不能直接進行 Base64 解碼,必須把前綴
data:*/*;base64,
從字串裡刪除以後,再進行解碼。
事件(Events)
var reader = new FileReader();
// 成功時促發
reader.addEventListener('load', function () {
console.log(reader.result);
});
// 成功失敗都會促發
reader.addEventListener('loadend', function () {
// ...
});
// 失敗時促發
reader.addEventListener('error', function () {
// ...
});
// reader.result contains the contents of blob as a typed array
reader.readAsArrayBuffer(blob);
範例
Sample Code @ CodePen
將圖片轉成 Base 64(image to Base64)
const uploader = document.querySelector('#uploader');
const previewImg = document.querySelector('#preview');
const fileReader = new FileReader();
uploader.addEventListener('change', (e) => {
const file = e.target.files[0]; // 取得 File Object
fileReader.readAsDataURL(file); // 將 File Object 讀取成 DataURL
});
// load 時可以取得 fileReader 回傳的結果
fileReader.addEventListener('load', function () {
const dataURL = fileReader.result; // Base64 Image
previewImg.src = dataURL;
});
Example Code @ CodePen
儲存 Blob(createObjectURL)
透過 window.URL.createObjectURL(blob)
可以取得 Blob URL,該網址的開頭會是 blob://
開頭,由於 Blob URL 就和一般的 URL 相同,因此可以直接透過 window.open(blobURL)
或建立成連結後進行下載:
const data = { number: 42, string: 'hello, world', date: new Date() };
var JSONData = [JSON.stringify(data)];
var JSONBlob = new Blob(JSONData, { type: 'application/json' });
saveData(JSONBlob, 'blob2json.json');
openData(JSONBlob);
function openData(blob) {
// 將 blob 放到 URL 上
url = window.URL.createObjectURL(blob);
window.open(url);
}
function saveData(blob, fileName) {
const a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
// 將 blob 放到 URL 上
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
// 釋放記憶體
a.href = '';
window.URL.revokeObjectURL(url);
}
參考:Saving binary data as file using JavaScript from a browser @ StackOverFlow
// 將 htmlBlob 儲存成實體檔
var htmlFragment = ['<div><h1>Hello</h1></div>'];
var htmlBlob = new Blob(htmlFragment, { type: 'text/html' });
saveData(htmlBlob, 'blob2html.html');
function saveData(blob, fileName) {
const a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
// 將 blob 放到 URL 上
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
// 釋放記憶體
a.href = '';
window.URL.revokeObjectURL(url);
}
建立 Blob URL
var blob = new Blob(['body { background-color: yellow; }'], { type: 'text/css' });
var link = document.createElement('link');
link.rel = 'stylesheet';
//createObjectURL returns a blob URL as a string.
link.href = window.URL.createObjectURL(blob);
document.body.appendChild(link);
直接打開 Blob URL
建立好 DataURI 後也可以使用 window.open
直接把它打開:
const buffer = new ArrayBuffer(2);
const bytes = new Uint8Array(buffer);
bytes[0] = 65;
bytes[1] = 66;
const blob = new Blob([buffer], { type: 'text/plain' });
const dataUri = window.URL.createObjectURL(blob);
window.open(dataUri);
將 Blob URL (dataURL) 轉回到 Blob
function dataURItoBlob(dataURI) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
var byteString = atob(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
// create a view into the buffer
var ia = new Uint8Array(ab);
// set the bytes of the buffer to the correct values
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
var blob = new Blob([ab], { type: mimeString });
return blob;
}
Blob from DataURL? @ StackOverflow
AJAX 請求 Blob
透過設定 xhr.responseType = 'blob'
可以請求回應的內容為 blob:
var xhr = new XMLHttpRequest();
xhr.open('GET', '/favicon.png');
xhr.responseType = 'blob'; //force the HTTP response, response-type header to be blob
xhr.onload = function () {
analyze_data(xhr.response); // the response will be a blob
};
xhr.send();
function analyze_data(blob) {
var myReader = new FileReader();
myReader.readAsArrayBuffer(blob);
myReader.addEventListener('loadend', function (e) {
var buffer = e.srcElement.result; // ArrayBuffer object
});
}
將 Blob 轉成 Buffer 後儲存
const blob = /* some blob */;
const buffer = await blob.arrayBuffer();
const bufferView = Buffer.from(buffer);
fs.writeFileSync('./foo', bufferView);
MIME Type
Multipurpose Internet Mail Extensions (MIME) type 是用來指稱檔案格式的標準化方式。瀏覽器通常使用 MIME type(而非 file extension)來決定要如何處理這份文件,因此,在伺服器的 response header 上設置適當地 MIME type 是很重要的。
Check all Media types @ IANA MIME Type @ MDN > Web > HTTP