前言
作為一名前端,在工作中會遇到很多二進制處理的問題。比如檔案的上傳、下載,都離不開對(File、Blob)資料的處理,但是大部分人對於這些都沒有一個清晰的認識。
本篇文章總結了瀏覽器端的二進制以及有關資料之間的轉化,整體內容有:
如何得到資料
認識File、Blob
如何對數據處理轉換
轉換後資料相互之間轉換
如何將處理後的資料進行還原
爲了更好的理解,總結了一張概括圖,如下所示:
下面針對每一個部分進行實踐操作。
1. 輸入
通常我們得到File
和Blob
物件,總結有這幾種方式:
透過input標籤
拖拽方式
canvas獲取
介面返回
如圖:
1.1 input
程式碼如下:
<input type="file" name="file" id="file"> const inputNode = document.getElementById('file') inputNode.addEventListener('change', (e) => { const file = e.target.files[0] })
得到File物件:
1.2 拖拽
程式碼如下:
<div id="drag"></div> const dragNode = document.getElementById("drag"); dragNode.ondragover = (e) => { e.preventDefault(); }; dragNode.ondrop = (e) => { e.preventDefault(); const files = e.dataTransfer.files; console.log(files); };
結果如下:
1.3 canvas
程式碼如下:
<canvas id="canvas"></canvas> const canvas = document.getElementById("canvas"); canvas.toBlob((blob) => { console.log("列印***blob", blob); });
結果如下:
1.4 介面
介面獲取需要設定響應頭為blob
,這裏以axios和fetch為例:
axios:
axios.post(url,{ // `responseType` 表示伺服器響應的資料型別,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' responseType: 'blob', })
fetch
fetch(url).then(res=>{ // 常用的有 json、text、arrayBuffer、blob return res.blob() })
2 資料
經過輸入得到File
或者Blob
,那麼兩者是什麼,有什麼關係,之間如何轉換。
2.1 Blob
Blob
全稱是binary large object
,即二進制大物件,它是 JavaScript 中的一個物件,表示原始的類似檔案的資料。
Blob 物件表示一個不可變、原始資料的類檔案物件。它的資料可以按文字或二進制的格式進行讀取,也可以轉換成 ReadableStream 來用於資料操作。
實際上,Blob 物件是包含有隻讀原始資料的類檔案物件。簡單來說,Blob 物件就是一個不可修改的二進制檔案。
2.1.1 建立blob
可以使用 Blob() 建構函式來建立一個 Blob:
new Blob(array, options);
有兩個引數:
array
:由ArrayBuffer
、ArrayBufferView
、Blob
、DOMString
等物件構成的,將會被放進Blob
;options
:可選的BlobPropertyBag
字典,它可能會指定如下兩個屬性type
:預設值為 "",表示將會被放入到blob
中的陣列內容的 MIME 型別。endings
:預設值為"transparent
",用於指定包含行結束符\n
的字串如何被寫入,不常用。
常見的 MIME 型別如下:
型別 | 描述 |
---|---|
text/plain | 純文字文件 |
text/html | HTML文件 |
text/javascript | JavaScript檔案 |
text/css | CSS檔案 |
application/json | JSON檔案 |
application/pdf | PDF檔案 |
application/xml | XML檔案 |
image/jpeg | JPEG影象 |
image/png | PNG影象 |
image/svg+xml | SVG影象 |
audio/mpeg | MP3檔案 |
video/mpeg | MP4檔案 |
2.2 File
File
物件提供有關檔案的資訊,並允許網頁中的 JavaScript 訪問其內容。實際上,File 物件是特殊型別的 Blob,且可以用在任意的 Blob 型別的 context 中。Blob 的屬性和方法都可以用於 File 物件。
注意:File 物件中只存在於瀏覽器環境中,在 Node.js 環境中不存在。
在 JavaScript 中,主要有兩種方法來獲取 File 物件:
<input>
元素上選擇檔案後返回的 FileList 物件;檔案拖放操作生成的
DataTransfer
物件;
2.3 總結
File
是一種特殊的Blob
,只存在於瀏覽器中,繼承Blob
中的方法和屬性。兩者如何轉換
File -> Blob:因為File是繼承於Blob,不用進行轉換
Blob -> File:透過
new File([blob],fileName,{type:MIME})
3. 轉換
透過Blob物件可以如何轉換成自己想要的資料型別,有兩種方式:
使用
createObjectURL
,生成Object URL
使用
FileReader
,生成DataURL
、BinaryString
、Text
、ArrayBuffer
下面分別實操下:
3.1 createObjectURL
URL
介面的 createObjectURL()
靜態方法建立一個用於表示引數中給出的物件的 URL 的字串。
URL 的生命週期與其建立時所在視窗的 document
繫結在一起。新物件 URL 代表指定的 File
物件或 Blob
物件。要釋放物件 URL,需呼叫 revokeObjectURL()
。
無論是File
還是Blob
,可以直接呼叫URL.createObjectURL
兩者的結果是相同的。
使用同一張圖片進行轉換為例:
File
透過input標籤獲取file物件,再進行轉換:
<input type="file" name="file" id="file" /> const inputNode = document.getElementById("file"); inputNode.addEventListener("change", (e) => { const file = e.target.files[0]; console.log( "列印***file", URL.createObjectURL(file) ); })
結果:
輸入瀏覽器預覽:
Blob
使用canvas的toBlob
方法得到blob,再進行轉換
<img src="./axios.png" alt="" srcset="" id="img" /> const cvs = document.createElement("canvas"); const imageNode = document.getElementById("img"); const ctx = cvs.getContext("2d"); cvs.width = imageNode.width; cvs.height = imageNode.height; ctx.drawImage(imageNode, 0, 0, cvs.width, cvs.height); cvs.toBlob((blob) => { console.log( "列印***blob", URL.createObjectURL(blob) ); });
結果:
輸入瀏覽器預覽:
3.1.1 Object URL
Object URL又稱Blob URL(W3C定義名稱),是HTML5中的新標準。它是一個用來表示File Object 或Blob Object 的URL。在網頁中,我們可能會看到過這種形式的 blob: URL
其實Blob URL/Object URL 是一種偽協議,允許將 Blob 和 File 物件用作影象、二進制資料下載連結等的 URL 源。
對於 Blob/File 物件,可以使用 URL建構函式的 createObjectURL()
方法建立將給出物件的 URL。這個 URL 物件表示指定的 File 物件或 Blob 物件。我們可以在<img>
、<script>
標籤中或者 <a>
和 <link>
標籤的 href
屬性中使用這個 URL。
經典下載程式碼:
// 使用 URL.createObjectURL 建立一個 URL 物件 const url = URL.createObjectURL(blob); // 建立一個 <a> 標籤並設定下載屬性 const a = document.createElement('a'); a.href = url; a.download = 'hello.txt'; // 設定下載的檔名 // 模擬點選 <a> 標籤 a.style.display = 'none'; document.body.appendChild(a); a.click(); // 移除 <a> 標籤並釋放 URL 物件 document.body.removeChild(a); URL.revokeObjectURL(url);
3.2 FileReader
FileReader
允許非同步讀取儲存在使用者計算機上的檔案(或原始資料緩衝區)的內容,使用 File
或 Blob
物件指定要讀取的檔案或資料。
3.2.1 Data URL
Data URL
是一種包含資料的URL,其中資料部分通常是使用Base64
編碼的。是一種將小資料嵌入到文件中的方式。它的格式如下:
data:[<mediatype>][;base64],<data>
<mediatype>
:表示資料的媒體型別(MIME 型別),例如text/plain
或image/png
。如果省略,預設值是text/plain;charset=US-ASCII
。;base64
:表示資料使用 Base64 編碼。如果省略,資料部分應為 URL 編碼的字串。<data>
:表示實際的資料部分。
3.2.1.1 與Base64的關係
Base64 是一種基於64個可列印字元來表示二進制資料的表示方法。Base64 編碼普遍應用於需要透過被設計為處理文字資料的媒介上儲存和傳輸二進制資料而需要編碼該二進制資料的場景。這樣是爲了保證資料的完整並且不用在傳輸過程中修改這些資料。
在 JavaScript 中,有兩個函式被分別用來處理解碼和編碼 base64 字串:
atob()
:解碼,解碼一個 Base64 字串;btoa()
:編碼,從一個字串或者二進制資料編碼一個 Base64 字串。
例如:一個包含純文字 "Hello, World!",使用btoa
轉換後得到的Data URL
可以表示為:
data:text/html;base64,SGVsbG8sIFdvcmxkIQ==
總結一下:
Data URL 是一種將資料嵌入到文件中的 URL 格式,其中資料部分通常使用 Base64 編碼。
Base64 是一種將二進制資料轉換為 ASCII 字串的編碼方式,用於在文字環境中傳輸二進制資料。
3.2.1.2 生成Data URL的兩種方式
canvas生成DataURL
const dataUrl = canvas.toDataURL();
結果如下:
FileReader的readAsDataURL
const reader = new FileReader(); // Read as Data URL reader.readAsDataURL(file); reader.onload = function (e) { console.log("列印***e,reader", e, reader); }
結果如下:
注意:使用e或者reader例項都可獲取結果
3.2.2 Text
const reader = new FileReader(); // Read as Data URL reader.readAsText(file); reader.onload = function () { console.log("列印***text", reader.result); }
新建txt檔案,寫入this is a text
。 結果如下:
注意:如果直接讀取圖片或其他檔案,亂碼
3.2.3 BinaryString
const reader = new FileReader(); // Read as Binary String reader.readAsBinaryString(file); reader.onload = function () { console.log("列印***binaryString", reader.result); }
結果如下:
3.2.4 ArrayBuffer
const reader = new FileReader(); // Read as ArrayBuffer reader.readAsArrayBuffer(file); reader.onload = function () { console.log("列印***binaryString", reader.result); }
結果如下:
4. 四種格式相互轉換
透過FileReader
轉換的四種資料,之間如何相互轉換。以下是將 Data URL
、ArrayBuffer
、Binary String
和 Text
之間相互轉換的具體實現:
4.1 Data URL轉換其他格式
4.1.1 Data URL -> ArrayBuffer
function dataURLToArrayBuffer(dataURL) { const binaryString = atob(dataURL.split(',')[1]); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; }
4.1.2 Data URL -> Binary String
function dataURLToBinaryString(dataURL) { return atob(dataURL.split(',')[1]); }
4.1.3 Data URL -> Text
async function dataURLToText(dataURL) { const arrayBuffer = dataURLToArrayBuffer(dataURL); return arrayBufferToText(arrayBuffer); }
4.2 ArrayBuffer轉換其他格式
4.2.1 ArrayBuffer -> Data URL
function arrayBufferToDataURL(arrayBuffer, mimeType = 'application/octet-stream') { const bytes = new Uint8Array(arrayBuffer); const binaryString = bytes.reduce((data, byte) => data + String.fromCharCode(byte), ''); return `data:${mimeType};base64,${btoa(binaryString)}`; }
4.2.2 ArrayBuffer -> Binary String
function arrayBufferToBinaryString(arrayBuffer) { const bytes = new Uint8Array(arrayBuffer); return bytes.reduce((data, byte) => data + String.fromCharCode(byte), ''); }
4.2.3 ArrayBuffer -> Text
function arrayBufferToText(arrayBuffer) { const decoder = new TextDecoder('utf-8'); return decoder.decode(new Uint8Array(arrayBuffer)); }
4.3 Binary String轉換其他格式
4.3.1 Binary String -> ArrayBuffer
function binaryStringToArrayBuffer(binaryString) { const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; }
4.3.2 Binary String -> Data URL
function binaryStringToDataURL(binaryString, mimeType = 'application/octet-stream') { return `data:${mimeType};base64,${btoa(binaryString)}`; }
4.3.3 Binary String -> Text
function binaryStringToText(binaryString) { return decodeURIComponent(escape(binaryString)); }
4.4 Text轉換其他格式
4.4.1 Text -> ArrayBuffer
function textToArrayBuffer(text) { const encoder = new TextEncoder(); return encoder.encode(text).buffer; }
4.4.2 Text -> Binary String
function textToBinaryString(text) { return unescape(encodeURIComponent(text)); }
4.4.3 Text -> Data URL
function textToDataURL(text, mimeType = 'text/plain') { const arrayBuffer = textToArrayBuffer(text); return arrayBufferToDataURL(arrayBuffer, mimeType); }
透過上述函式,可以實現 Data URL
、ArrayBuffer
、Binary String
和 Text
之間的相互轉換。注意在處理不同編碼格式和資料型別時,需要根據具體需求調整 TextDecoder
和 TextEncoder
的編碼引數。
5. FileReader轉換後四種格式轉換為blob格式
要將 FileReader
轉換後的四種格式(Data URL
、ArrayBuffer
、Binary String
和 Text
)轉換為 Blob
格式,可以使用以下方法:
5.1 Data URL -> Blob
function dataURLToBlob(dataURL) { const byteString = atob(dataURL.split(',')[1]); const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; const ab = new ArrayBuffer(byteString.length); const ia = new Uint8Array(ab); for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ab], { type: mimeString }); }
5.2 ArrayBuffer -> Blob
function arrayBufferToBlob(arrayBuffer, mimeType = 'application/octet-stream') { return new Blob([arrayBuffer], { type: mimeType }); }
5.3 Binary String -> Blob
function binaryStringToBlob(binaryString, mimeType = 'application/octet-stream') { const ab = new ArrayBuffer(binaryString.length); const ia = new Uint8Array(ab); for (let i = 0; i < binaryString.length; i++) { ia[i] = binaryString.charCodeAt(i); } return new Blob([ab], { type: mimeType }); }
5.4 Text -> Blob
function textToBlob(text, mimeType = 'text/plain') { return new Blob([text], { type: mimeType }); }
具體示例:
// Data URL to Blob const dataURL = 'data:text/plain;base64,SGVsbG8gd29ybGQ='; const blobFromDataURL = dataURLToBlob(dataURL); // ArrayBuffer to Blob const arrayBuffer = new Uint8Array([72, 101, 108, 108, 111]).buffer; const blobFromArrayBuffer = arrayBufferToBlob(arrayBuffer); // Binary String to Blob const binaryString = 'Hello'; const blobFromBinaryString = binaryStringToBlob(binaryString); // Text to Blob const text = 'Hello world'; const blobFromText = textToBlob(text);
總結
透過上述整理可得,對於從檔案到資料的轉換可以形成一個完整的閉環。從檔案-> 輸入-> 資料 -> 轉換能夠形成一個直觀的感受。