Blob(Binary Large Object)表示二進制類型的大對象。在數據庫管理系統中,將二進制數據存儲爲一個單一個體的集合。Blob 一般是影像、聲音或多媒體文件。在 JavaScript 中 Blob 類型的對象表示不可變的相似文件對象的原始數據。 爲了更直觀的感覺 Blob 對象,咱們先來使用 Blob 構造函數,建立一個 myBlob 對象,具體以下圖所示:javascript
如你所見,myBlob 對象含有兩個屬性:size 和 type。其中 size
屬性用於表示數據的大小(以字節爲單位),type
是 MIME 類型的字符串。Blob 表示的不必定是 JavaScript 原生格式的數據。好比 File
接口基於 Blob
,繼承了 blob 的功能並將其擴展使其支持用戶系統上的文件。html
建立了一個 「重學TypeScript」 的微信羣,想加羣的小夥伴,加我微信 "semlinker",備註重學TS。 目前已出 TS 專題 39 篇。
Blob
由一個可選的字符串 type
(一般是 MIME 類型)和 blobParts
組成:前端
MIME(Multipurpose Internet Mail Extensions)多用途互聯網郵件擴展類型,是設定某種擴展名的文件用一種應用程序來打開的方式類型,當該擴展名文件被訪問的時候,瀏覽器會自動使用指定應用程序來打開。多用於指定一些客戶端自定義的文件名,以及一些媒體文件打開方式。常見的 MIME 類型有:超文本標記語言文本 .html text/html、PNG圖像 .png image/png、普通文本 .txt text/plain 等。java
Blob 構造函數的語法爲:git
var aBlob = new Blob(blobParts, options);
相關的參數說明以下:github
options:一個可選的對象,包含如下兩個屬性:web
""
,它表明了將會被放入到 blob 中的數組內容的 MIME 類型。"transparent"
,用於指定包含行結束符 \n
的字符串如何被寫入。 它是如下兩個值中的一個: "native"
,表明行結束符會被更改成適合宿主操做系統文件系統的換行符,或者 "transparent"
,表明會保持 blob 中保存的結束符不變。示例一:從字符串建立 Blobshell
let myBlobParts = ['<html><h2>Hello Semlinker</h2></html>']; // an array consisting of a single DOMString let myBlob = new Blob(myBlobParts, {type : 'text/html', endings: "transparent"}); // the blob console.log(myBlob.size + " bytes size"); // Output: 37 bytes size console.log(myBlob.type + " is the type"); // Output: text/html is the type
示例二:從類型化數組和字符串建立 Blob數據庫
let hello = new Uint8Array([72, 101, 108, 108, 111]); // 二進制格式的 "hello" let blob = new Blob([hello, ' ', 'semlinker'], {type: 'text/plain'});
介紹完 Blob 構造函數,接下來咱們來分別介紹 Blob 類的屬性和方法:express
前面咱們已經知道 Blob 對象包含兩個屬性:
Blob
對象中所包含數據的大小(以字節爲單位)。Blob
對象所包含數據的 MIME 類型。若是類型未知,則該值爲空字符串。ReadableStream
。USVString
。ArrayBuffer
。這裏咱們須要注意的是,Blob
對象是不可改變的。咱們不能直接在一個 Blob 中更改數據,可是咱們能夠對一個 Blob 進行分割,從其中建立新的 Blob 對象,將它們混合到一個新的 Blob 中。這種行爲相似於 JavaScript 字符串:咱們沒法更改字符串中的字符,但能夠建立新的更正後的字符串。
File 對象是特殊類型的 Blob,且能夠用在任意的 Blob 類型的上下文中。因此針對大文件傳輸的場景,咱們可使用 slice 方法對大文件進行切割,而後分片進行上傳,具體示例以下:
const file = new File(["a".repeat(1000000)], "test.txt"); const chunkSize = 40000; const url = "https://httpbin.org/post"; async function chunkedUpload() { for (let start = 0; start < file.size; start += chunkSize) { const chunk = file.slice(start, start + chunkSize + 1); const fd = new FormData(); fd.append("data", chunk); await fetch(url, { method: "post", body: fd }).then((res) => res.text() ); } }
咱們可使用如下方法從互聯網上下載數據並將數據存儲到 Blob 對象中,好比:
const downloadBlob = (url, callback) => { const xhr = new XMLHttpRequest() xhr.open('GET', url) xhr.responseType = 'blob' xhr.onload = () => { callback(xhr.response) } xhr.send(null) }
固然除了使用 XMLHttpRequest
API 以外,咱們也可使用 fetch
API 來實現以流的方式獲取二進制數據。這裏咱們來看一下如何使用 fetch API 獲取線上圖片並本地顯示,具體實現以下:
const myImage = document.querySelector('img'); const myRequest = new Request('flowers.jpg'); fetch(myRequest) .then(function(response) { return response.blob(); }) .then(function(myBlob) { let objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; });
當 fetch 請求成功的時候,咱們調用 response 對象的 blob()
方法,從 response 對象中讀取一個 Blob 對象,而後使用 createObjectURL()
方法建立一個 objectURL,而後把它賦值給 img
元素的 src
屬性從而顯示這張圖片。
Blob 能夠很容易的做爲 <a>
、<img>
或其餘標籤的 URL,多虧了 type
屬性,咱們也能夠上傳/下載 Blob
對象。下面咱們將舉一個 Blob 文件下載的示例,不過在看具體示例前咱們得簡單介紹一下 Blob URL。
1.Blob URL/Object URL
Blob URL/Object URL 是一種僞協議,容許 Blob 和 File 對象用做圖像,下載二進制數據連接等的 URL 源。在瀏覽器中,咱們使用 URL.createObjectURL
方法來建立 Blob URL,該方法接收一個 Blob
對象,併爲其建立一個惟一的 URL,其形式爲 blob:<origin>/<uuid>
,對應的示例以下:
blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641
瀏覽器內部爲每一個經過 URL.createObjectURL
生成的 URL 存儲了一個 URL → Blob 映射。所以,此類 URL 較短,但能夠訪問 Blob
。生成的 URL 僅在當前文檔打開的狀態下才有效。它容許引用 <img>
、<a>
中的 Blob
,但若是你訪問的 Blob URL 再也不存在,則會從瀏覽器中收到 404 錯誤。
上述的 Blob URL 看似很不錯,但實際上它也有反作用。雖然存儲了 URL → Blob 的映射,但 Blob 自己仍駐留在內存中,瀏覽器沒法釋放它。映射在文檔卸載時自動清除,所以 Blob 對象隨後被釋放。可是,若是應用程序壽命很長,那不會很快發生。所以,若是咱們建立一個 Blob URL,即便再也不須要該 Blob,它也會存在內存中。
針對這個問題,咱們能夠調用 URL.revokeObjectURL(url)
方法,從內部映射中刪除引用,從而容許刪除 Blob(若是沒有其餘引用),並釋放內存。接下來,咱們來看一下 Blob 文件下載的具體示例。
2.Blob 文件下載示例
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Blob 文件下載示例</title> </head> <body> <button id="downloadBtn">文件下載</button> <script src="index.js"></script> </body> </html>
index.js
const download = (fileName, blob) => { const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = fileName; link.click(); link.remove(); URL.revokeObjectURL(link.href); }; const downloadBtn = document.querySelector("#downloadBtn"); downloadBtn.addEventListener("click", (event) => { const fileName = "blob.txt"; const myBlob = new Blob(["一文完全掌握 Blob Web API"], { type: "text/plain" }); download(fileName, myBlob); });
在示例中,咱們經過調用 Blob 的構造函數來建立類型爲 "text/plain" 的 Blob 對象,而後經過動態建立 a
標籤來實現文件的下載。
URL.createObjectURL
的一個替代方法是,將 Blob
轉換爲 base64 編碼的字符串。Base64 是一種基於 64 個可打印字符來表示二進制數據的表示方法,它經常使用於在處理文本數據的場合,表示、傳輸、存儲一些二進制數據,包括 MIME 的電子郵件及 XML 的一些複雜數據。
在 MIME 格式的電子郵件中,base64 能夠用來將二進制的字節序列數據編碼成 ASCII 字符序列構成的文本。使用時,在傳輸編碼方式中指定 base64。使用的字符包括大小寫拉丁字母各 26 個、數字 10 個、加號 + 和斜槓 /,共 64 個字符,等號 = 用來做爲後綴用途。
下面咱們來介紹如何在 HTML 中嵌入 base64 編碼的圖片。在編寫 HTML 網頁時,對於一些簡單圖片,一般會選擇將圖片內容直接內嵌在網頁中,從而減小沒必要要的網絡請求,可是圖片數據是二進制數據,該怎麼嵌入呢?絕大多數現代瀏覽器都支持一種名爲 Data URLs
的特性,容許使用 base64 對圖片或其餘文件的二進制數據進行編碼,將其做爲文本字符串嵌入網頁中。
Data URLs 由四個部分組成:前綴(data:
)、指示數據類型的 MIME 類型、若是非文本則爲可選的 base64
標記、數據自己:
data:[<mediatype>][;base64],<data>
mediatype
是個 MIME 類型的字符串,例如 "image/jpeg
" 表示 JPEG 圖像文件。若是被省略,則默認值爲 text/plain;charset=US-ASCII
。若是數據是文本類型,你能夠直接將文本嵌入(根據文檔類型,使用合適的實體字符或轉義字符)。若是是二進制數據,你能夠將數據進行 base64 編碼以後再進行嵌入。好比嵌入一張圖片:
<img alt="logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...">
但須要注意的是:若是圖片較大,圖片的色彩層次比較豐富,則不適合使用這種方式,由於該圖片通過 base64 編碼後的字符串很是大,會明顯增大 HTML 頁面的大小,從而影響加載速度。 除此以外,利用 FileReader API,咱們也能夠方便的實現圖片本地預覽功能,具體代碼以下:
<input type="file" accept="image/*" onchange="loadFile(event)"> <img id="output"/> <script> const loadFile = function(event) { const reader = new FileReader(); reader.onload = function(){ const output = document.querySelector('output'); output.src = reader.result; }; reader.readAsDataURL(event.target.files[0]); }; </script>
在以上示例中,咱們爲 file 類型輸入框綁定 onchange
事件處理函數 loadFile
,在該函數中,咱們建立了一個 FileReader 對象併爲該對象綁定 onload
相應的事件處理函數,而後調用 FileReader 對象的 readAsDataURL()
方法,把本地圖片對應的 File 對象轉換爲 Data URL。
在完成本地圖片預覽以後,咱們能夠直接把圖片對應的 Data URLs 數據提交到服務器。針對這種情形,服務端須要作一些相關處理,才能正常保存上傳的圖片,這裏以 Express 爲例,具體處理代碼以下:
const app = require('express')(); app.post('/upload', function(req, res){ let imgData = req.body.imgData; // 獲取POST請求中的base64圖片數據 let base64Data = imgData.replace(/^data:image\/\w+;base64,/, ""); let dataBuffer = Buffer.from(base64Data, 'base64'); fs.writeFile("image.png", dataBuffer, function(err) { if(err){ res.send(err); }else{ res.send("圖片上傳成功!"); } }); });
對於 FileReader 對象來講,除了支持把 Blob/File 對象轉換爲 Data URL 以外,它還提供了 readAsArrayBuffer()
和 readAsText()
方法,用於把 Blob/File 對象轉換爲其它的數據格式。這裏咱們來看個 readAsArrayBuffer()
的使用示例:
// 從 blob 獲取 arrayBuffer let fileReader = new FileReader(); fileReader.onload = function(event) { let arrayBuffer = fileReader.result; }; fileReader.readAsArrayBuffer(blob);
在一些場合中,咱們但願在上傳本地圖片時,先對圖片進行必定的壓縮,而後再提交到服務器,從而減小傳輸的數據量。在前端要實現圖片壓縮,咱們能夠利用 Canvas 對象提供的 toDataURL()
方法,該方法接收 type
和 encoderOptions
兩個可選參數。
其中 type
表示圖片格式,默認爲 image/png
。而 encoderOptions
用於表示圖片的質量,在指定圖片格式爲 image/jpeg
或 image/webp
的狀況下,能夠從 0 到 1 的區間內選擇圖片的質量。若是超出取值範圍,將會使用默認值 0.92
,其餘參數會被忽略。
下面咱們來看一下具體如何實現圖片壓縮:
// compress.js const MAX_WIDTH = 800; // 圖片最大寬度 function compress(base64, quality, mimeType) { let canvas = document.createElement("canvas"); let img = document.createElement("img"); img.crossOrigin = "anonymous"; return new Promise((resolve, reject) => { img.src = base64; img.onload = () => { let targetWidth, targetHeight; if (img.width > MAX_WIDTH) { targetWidth = MAX_WIDTH; targetHeight = (img.height * MAX_WIDTH) / img.width; } else { targetWidth = img.width; targetHeight = img.height; } canvas.width = targetWidth; canvas.height = targetHeight; let ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, targetWidth, targetHeight); // 清除畫布 ctx.drawImage(img, 0, 0, canvas.width, canvas.height); let imageData = canvas.toDataURL(mimeType, quality / 100); resolve(imageData); }; }); }
對於返回的 Data URL 格式的圖片數據,爲了進一步減小傳輸的數據量,咱們能夠把它轉換爲 Blob 對象:
function dataUrlToBlob(base64, mimeType) { let bytes = window.atob(base64.split(",")[1]); let ab = new ArrayBuffer(bytes.length); let ia = new Uint8Array(ab); for (let i = 0; i < bytes.length; i++) { ia[i] = bytes.charCodeAt(i); } return new Blob([ab], { type: mimeType }); }
在轉換完成後,咱們就能夠壓縮後的圖片對應的 Blob 對象封裝在 FormData 對象中,而後再經過 AJAX 提交到服務器上:
function uploadFile(url, blob) { let formData = new FormData(); let request = new XMLHttpRequest(); formData.append("image", blob); request.open("POST", url, true); request.send(formData); }
其實 Canvas 對象除了提供 toDataURL()
方法以外,它還提供了一個 toBlob()
方法,該方法的語法以下:
canvas.toBlob(callback, mimeType, qualityArgument)
和 toDataURL()
方法相比,toBlob()
方法是異步的,所以多了個 callback
參數,這個 callback
回調方法默認的第一個參數就是轉換好的 blob
文件信息。
介紹完上述的內容,咱們來看一下本地圖片壓縮完整的示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>本地圖片壓縮</title> </head> <body> <input type="file" accept="image/*" onchange="loadFile(event)" /> <script src="./compress.js"></script> <script> const loadFile = function (event) { const reader = new FileReader(); reader.onload = async function () { let compressedDataURL = await compress( reader.result, 90, "image/jpeg" ); let compressedImageBlob = dataUrlToBlob(compressedDataURL); uploadFile("https://httpbin.org/post", compressedImageBlob); }; reader.readAsDataURL(event.target.files[0]); }; </script> </body> </html>
PDF(便攜式文件格式,Portable Document Format)是由 Adobe Systems 在 1993 年用於文件交換所發展出的文件格式。在瀏覽器端,利用一些現成的開源庫,好比 jsPDF,咱們也能夠方便地生成 PDF 文檔。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>客戶端生成 PDF 示例</title> </head> <body> <h3>客戶端生成 PDF 示例</h3> <script src="https://unpkg.com/jspdf@latest/dist/jspdf.min.js"></script> <script> (function generatePdf() { const doc = new jsPDF(); doc.text("Hello semlinker!", 66, 88); const blob = new Blob([doc.output()], { type: "application/pdf" }); blob.text().then((blobAsText) => { console.log(blobAsText); }); })(); </script> </body> </html>
在以上示例中,咱們首先建立 PDF 文檔對象,而後調用該對象上的 text()
方法在指定的座標點上添加 Hello semlinker!
文本,而後咱們利用生成的 PDF 內容來建立對應的 Blob 對象,須要注意的是咱們設置 Blob 的類型爲 application/pdf
,最後咱們把 Blob 對象中保存的內容轉換爲文本並輸出到控制檯。因爲內容較多,這裏咱們只列出少部分輸出結果:
%PDF-1.3 %ºß¬à 3 0 obj <</Type /Page /Parent 1 0 R /Resources 2 0 R /MediaBox [0 0 595.28 841.89] /Contents 4 0 R >> endobj ....
其實 jsPDF 除了支持純文本以外,它也能夠生成帶圖片的 PDF 文檔,好比:
let imgData = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/...' let doc = new jsPDF(); doc.setFontSize(40) doc.text(35, 25, 'Paranyan loves jsPDF') doc.addImage(imgData, 'JPEG', 15, 40, 180, 160)
Blob 的應用場景還不少,這裏咱們就不一一列舉了,感興趣的小夥伴能夠自行查閱相關資料。
ArrayBuffer 對象用於表示通用的,固定長度的原始二進制數據緩衝區。你不能直接操縱 ArrayBuffer 的內容,而是須要建立一個類型化數組對象或 DataView 對象,該對象以特定格式表示緩衝區,並使用該對象讀取和寫入緩衝區的內容。
Blob 類型的對象表示不可變的相似文件對象的原始數據。Blob 表示的不必定是 JavaScript 原生格式的數據。File 接口基於 Blob,繼承了Blob 功能並將其擴展爲支持用戶系統上的文件。
window.URL.createObjectURL()
。可是,你可能仍須要 FileReader 之類的 File API 才能與 Blob 一塊兒使用。Blob 與 ArrayBuffer 對象之間是能夠相互轉化的:
readAsArrayBuffer()
方法,能夠把 Blob 對象轉換爲 ArrayBuffer 對象;new Blob([new Uint8Array(data]);
,能夠把 ArrayBuffer 對象轉換爲 Blob 對象。對於 HTTP 的場景,好比在 AJAX 場景下,Blob 和 ArrayBuffer 能夠經過如下方式來使用:
function GET(url, callback) { let xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; // or xhr.responseType = "blob"; xhr.send(); xhr.onload = function(e) { if (xhr.status != 200) { alert("Unexpected status code " + xhr.status + " for " + url); return false; } callback(new Uint8Array(xhr.response)); // or new Blob([xhr.response]); }; }
瞭解完上述的內容,相信有的讀者可能會以爲意猶未盡。那麼,對於 Blob 來講還有哪些內容能夠繼續深刻學習的呢?本人下一步的計劃是基於 Deno 的源碼,來逐步分析 DenoBlob 的具體實現。固然也會順便分析一下 URL.createObjectURL()
方法和 revokeObjectURL()
方法的實現。
本人的全棧修仙之路訂閱號,會按期分享 Angular、TypeScript、Node.js 相關文章,歡迎感興趣的小夥伴訂閱哈!