ArrayBuffer
和檢視是 ECMA 標準的一部分,也是 JavaScript 的一部分。
在瀏覽器中,有額外的較高層級物件,在 檔案 API 中說明,特別是 Blob
。
Blob
包含一個選用的字串 type
(通常是 MIME 類型),加上 blobParts
– 其他 Blob
物件、字串和 BufferSource
的順序。
建構函式語法為
new Blob(blobParts, options);
blobParts
是Blob
/BufferSource
/String
值的陣列。options
選用的物件type
–Blob
類型,通常是 MIME 類型,例如image/png
,endings
– 是否轉換行尾,讓Blob
對應到目前作業系統的新行(\r\n
或\n
)。預設為"transparent"
(不做任何事),但也可以是"native"
(轉換)。
例如
// create Blob from a string
let blob = new Blob(["<html>…</html>"], {type: 'text/html'});
// please note: the first argument must be an array [...]
// create Blob from a typed array and strings
let hello = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in binary form
let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});
我們可以使用 Blob
切片
blob.slice([byteStart], [byteEnd], [contentType]);
byteStart
– 起始位元組,預設為 0。byteEnd
– 最後一個位元組(不包含,預設為最後一個)。contentType
– 新 blob 的type
,預設與來源相同。
引數類似於 array.slice
,負數也是允許的。
Blob
物件是不可變的我們無法直接在 Blob
中變更資料,但我們可以切片 Blob
的部分,從中建立新的 Blob
物件,將它們混合到新的 Blob
中,以此類推。
此行為類似於 JavaScript 字串:我們無法變更字串中的字元,但我們可以建立一個新的更正字串。
Blob 作為 URL
Blob 可以輕鬆用作 <a>
、<img>
或其他標籤的 URL,以顯示其內容。
由於有 type
,我們也可以下載/上傳 Blob
物件,而 type
自然會在網路要求中變成 Content-Type
。
讓我們從一個簡單的範例開始。透過按一下連結,您可以下載一個動態產生的 Blob
,其內容為 hello world
,作為一個檔案
<!-- download attribute forces the browser to download instead of navigating -->
<a download="hello.txt" href='#' id="link">Download</a>
<script>
let blob = new Blob(["Hello, world!"], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
</script>
我們也可以在 JavaScript 中動態建立一個連結,並透過 link.click()
模擬按一下,然後下載就會自動開始。
以下是類似的程式碼,會導致使用者下載動態建立的 Blob
,而不需要任何 HTML
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(link.href);
URL.createObjectURL
會取得一個 Blob
,並為它建立一個唯一的 URL,格式為 blob:<origin>/<uuid>
。
這就是 link.href
的值
blob:https://javascriptinfo.dev.org.tw/1e67e00e-860d-40a5-89ae-6ab0cbee6273
對於每個由 URL.createObjectURL
產生的 URL,瀏覽器會在內部儲存一個 URL → Blob
的對應。因此,此類 URL 很短,但允許存取 Blob
。
產生的 URL(以及連結)僅在目前的文件開啟時有效。它允許在 <img>
、<a>
中參照 Blob
,基本上任何其他需要 URL 的物件都可以。
不過有一個副作用。當 Blob
有對應時,Blob
本身會駐留在記憶體中。瀏覽器無法釋放它。
文件卸載時會自動清除對應,因此 Blob
物件會在當時釋放。但如果應用程式執行時間很長,這就不會很快發生。
因此,如果我們建立一個 URL,即使不再需要,那個 Blob
仍會掛在記憶體中。
URL.revokeObjectURL(url)
會從內部對應中移除參考,因此允許刪除 Blob
(如果沒有其他參考),並釋放記憶體。
在最後一個範例中,我們打算只使用 Blob
一次,用於立即下載,因此我們立即呼叫 URL.revokeObjectURL(link.href)
。
在前面那個有可按 HTML 連結的範例中,我們沒有呼叫 URL.revokeObjectURL(link.href)
,因為那會讓 Blob
url 無效。在撤銷後,由於對應已移除,URL 就無法再運作了。
Blob 轉為 base64
URL.createObjectURL
的替代方案是將 Blob
轉換為 base64 編碼字串。
這種編碼將二進位資料表示為超安全「可讀取」字元的字串,其 ASCII 碼從 0 到 64。更重要的是,我們可以在「資料 URL」中使用這種編碼。
資料 URL 的格式為 data:[<mediatype>][;base64],<data>
。我們可以在任何地方使用這種 URL,與「一般」URL 相同。
例如,這裡有一個笑臉
<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">
瀏覽器會解碼字串並顯示圖片:
要將 Blob
轉換為 base64,我們將使用內建的 FileReader
物件。它可以從多種格式的 Blob 中讀取資料。在 下一章 中,我們將更深入地探討它。
以下是透過 base-64 下載 blob 的示範
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
let reader = new FileReader();
reader.readAsDataURL(blob); // converts the blob to base64 and calls onload
reader.onload = function() {
link.href = reader.result; // data url
link.click();
};
建立 Blob
的 URL 的這兩種方法都可以使用。但通常 URL.createObjectURL(blob)
比較簡單且快速。
- 如果關心記憶體,我們需要撤銷它們。
- 直接存取 blob,沒有「編碼/解碼」
- 不需要撤銷任何東西。
- 編碼時,大型
Blob
物件會造成效能和記憶體損失。
圖片轉為 blob
我們可以建立圖片、圖片部分的 Blob
,甚至製作網頁截圖。這對於將其上傳到某個地方很有用。
圖片操作是透過 <canvas>
元素完成的
- 使用 canvas.drawImage 在畫布上繪製影像(或其部分)。
- 呼叫畫布方法 .toBlob(callback, format, quality),它會建立一個
Blob
,並在完成時執行callback
。
在以下範例中,僅複製影像,但我們可以在建立 blob 之前,從中剪切或在畫布上轉換它。
// take any image
let img = document.querySelector('img');
// make <canvas> of the same size
let canvas = document.createElement('canvas');
canvas.width = img.clientWidth;
canvas.height = img.clientHeight;
let context = canvas.getContext('2d');
// copy image to it (this method allows to cut image)
context.drawImage(img, 0, 0);
// we can context.rotate(), and do many other things on canvas
// toBlob is async operation, callback is called when done
canvas.toBlob(function(blob) {
// blob ready, download it
let link = document.createElement('a');
link.download = 'example.png';
link.href = URL.createObjectURL(blob);
link.click();
// delete the internal blob reference, to let the browser clear memory from it
URL.revokeObjectURL(link.href);
}, 'image/png');
如果我們偏好使用 async/await
,而不是 callback
let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
對於螢幕擷取,我們可以使用像是 https://github.com/niklasvh/html2canvas 的函式庫。它的作用就是瀏覽頁面並在 <canvas>
上繪製它。然後,我們可以像上述方式一樣取得它的 Blob
。
從 Blob 到 ArrayBuffer
Blob
建構函式允許從幾乎任何東西建立 blob,包括任何 BufferSource
。
但是,如果我們需要執行低階處理,我們可以從 blob.arrayBuffer()
取得最低階的 ArrayBuffer
。
// get arrayBuffer from blob
const bufferPromise = await blob.arrayBuffer();
// or
blob.arrayBuffer().then(buffer => /* process the ArrayBuffer */);
從 Blob 到串流
當我們讀取和寫入超過 2 GB
的 blob 時,使用 arrayBuffer
對我們來說會更耗費記憶體。此時,我們可以直接將 blob 轉換為串流。
串流是一個特殊物件,允許逐部分讀取(或寫入)它。它不在我們的討論範圍內,但這裡有一個範例,你可以在 https://developer.mozilla.org/en-US/docs/Web/API/Streams_API 閱讀更多資訊。串流適用於適合逐部分處理的資料。
Blob
介面的 stream()
方法會傳回一個 ReadableStream
,它在讀取時會傳回 Blob
中包含的資料。
然後,我們可以像這樣從中讀取
// get readableStream from blob
const readableStream = blob.stream();
const stream = readableStream.getReader();
while (true) {
// for each iteration: value is the next blob fragment
let { done, value } = await stream.read();
if (done) {
// no more data in the stream
console.log('all blob processed.');
break;
}
// do something with the data portion we've just read from the blob
console.log(value);
}
摘要
雖然 ArrayBuffer
、Uint8Array
和其他 BufferSource
是「二進位資料」,但 Blob 代表「具有類型二進位資料」。
這使得 Blob 適用於瀏覽器中常見的上傳/下載作業。
執行網路要求的方法,例如 XMLHttpRequest、fetch 等,可以原生支援 Blob
,以及其他二進位類型。
我們可以輕鬆地在 Blob
和低階二進位資料類型之間進行轉換
- 我們可以使用
new Blob(...)
建構函式,從型別化陣列建立Blob
。 - 我們可以使用
blob.arrayBuffer()
從 Blob 取得ArrayBuffer
,然後在它上面建立檢視,以進行低階二進位處理。
當我們需要處理大型 blob 時,轉換串流非常有用。你可以輕鬆地從 blob 建立 ReadableStream
。Blob
介面的 stream()
方法會傳回一個 ReadableStream
,它在讀取時會傳回 blob 中包含的資料。
留言
<code>
標籤;要插入多行程式碼,請將其包覆在<pre>
標籤中;要插入超過 10 行的程式碼,請使用沙盒 (plnkr、jsbin、codepen…)