Javascript中的二進制數據類型

原文:Javascript中的二進制數據類型javascript

最近工做上接手的一個項目涉及到了圖片的加密壓縮與上傳,也就須要用JavaScript直接去操做二進制數據,特意整理了一下JavaScript中的二進制數據類型,當作學習筆記。html

起源

歷史上JavaScript是沒有讀寫二進制數據能力的,但隨着es5中Blob對象的引入以及es6中ArrayBuffer對象、TypedArray和DataView對象的規範化,js處理二進制數據的能力大幅度加強,也能直接處理文件流,網絡流等二進制Buffer數據了。java

基本的二進制數據類型

ArrayBuffer

ArrayBuffer表明內存當中的一段通用的、固定長度的原始二進制數據緩衝區。它沒法被直接讀寫,須要配合TypedArray對象或DataView對象來操做緩衝區的內容。es6

咱們能夠直接經過ArrayBuffer構造函數來建立一段存放二進制數據的連續內存區域。web

const buffer = new ArrayBuffer(8);
複製代碼

這裏咱們建立了一個長度爲8個字節的連續內存區域。因爲咱們沒有賦初值,每個字節單元的值都默認是0.(這裏注意ArrayBuffer不能像數組那樣直接經過索引下標來訪問內存單元的數據)json

ArrayBuffer有一個只讀的byteLength屬性能夠讀取數組的字節長度,這在ArrayBuffer建立的時候就已經肯定。ArrayBuffer還有一個ArrayBuffer.prototype.slice方法用來拷貝自身的一部分生成一個新的ArrayBuffer。canvas

const buffer = new ArrayBuffer(8);
buffer.byteLength  // 8
const buf = buffer.slice(0, 4);
buf.byteLength // 4
複製代碼

但ArrayBuffer自身是不可直接讀寫的,更多操做仍是須要依靠TypedArrayDataView來實現。api

TypedArray

咱們經過ArrayBuffer在內存中存儲一段未加工的二進制數據,這一段二進制數據自己只是內存中存儲的0,1數據,咱們經過不一樣的格式來解讀就會賦予它不一樣的意義,這裏的格式也就是‘視圖’,視圖有兩種,一種是TypedArray,一種是DataView數組

TypedArray是一簇api類似的Array-like對象,如今包括如下九種:瀏覽器

  • Int8Array();
  • Uint8Array();
  • Uint8ClampedArray();
  • Int16Array();
  • Uint16Array();
  • Int32Array();
  • Uint32Array();
  • Float32Array();
  • Float64Array();

生成視圖能夠採用多種方法來。

// TypedArray 不是實際存在的對象,指代以上九種構造函數之一
new TypedArray(length);
new TypedArray(typedArray);
new TypedArray(object);
new TypedArray(buffer [, byteOffset [, length]]);
複製代碼

以Int8Array爲例,咱們建立一個以8bit爲單位存儲單元的內存數組。

// 在已存在的ArrayBuffer 上生成視圖
const buffer = new ArrayBuffer(4);
const x1 = new Int8Array(buffer);
x1  // [0, 0, 0, 0]

// 不借助事先生成的ArrayBuffer, 直接生成視圖
const x2 = new Int8Array(4);
x2[0] = 1;
x2  // [1, 0, 0, 0]

// 經過已有的TypedArray轉化而來
const x3 = new Int8Array(4);
x3[0] = 1;
const x4 = new Int8Array(x3);
x4[0] // 1
x4[0] = 2;
x3[0], x4[0]  // 1, 2

// 經過對象轉化,相似於Array.from方法
const x5 = new Int8Array([1, 2, 3, 4]);
x5  // [1, 2, 3, 4]
複製代碼

TypedArray表現和Array相似,能夠用方括號運算符([])訪問特定內存單元的數據,也可使用幾乎全部的數組方法,具體能夠參考MDN

TypedArray還有幾個特有的屬性與方法。

TypedArray.prototype.buffer: 指向咱們操做的TypedArray引用的ArrayBuffer; TypedArray.prototype.byteLength: 返回ArrayBuffer的字節長度; TypedArray.prototype.byteOffset: 讀取ArrayBuffer時的字節偏移量; TypedArray.prototype.set: 複製一個指定數組的元素保存到TypedArray; TypedArray.prototype.subarray: 拷貝TypedArray的一個子數組爲副本;

const buffer = new ArrayBuffer(4);
const int8Arr = new Int8Array(buffer);
int8Arr.buffer === buffer;  // true
int8Arr.byteLength  // 4
int8Arr.byteOffset  // 0

const int8Arr2 = new Int8Array(4);
int8Arr2.set(int8Arr);  // 以int8Arr爲數據來源製做了一個相同數據的副本
int8Arr.subarray(2, 3);  // 返回以int8Arr中索引從2(含)到3(不含)的數據爲數據來源的副本
複製代碼

DataView

不一樣於類型化數組,一個數組只能存放同一類型的數據,DataView能夠在內存中存放不一樣類型的數據。但TypedArray的一系列方法足以應付各類場景下的ArrayBuffer存取操做,須要用到DataView的場景較少,這裏簡單介紹下。

DataView對象接受一個ArrayBuffer生成視圖。

// new DataView(buffer [, byteOffset [, length]]);
const buffer = new ArrayBuffer(4);
const dv = new DataView(buffer);
複製代碼

DataView有十六個實例方法對應於8種數據類型的存取操做。

  • getInt8()
  • getUint8()
  • getInt16()
  • getUint16()
  • getInt32()
  • getUint32()
  • getFloat32()
  • getFloat64()
  • setInt8()
  • setUint8()
  • setInt16()
  • setUint16()
  • setInt32()
  • setUint32()
  • setFloat32()
  • setFloat64()
const buffer = new ArrayBuffer(4);
const dv = new DataView(buffer, 0);
dv.setInt8(0, 4);
dv.getInt8(0);
複製代碼

web瀏覽器中的二進制對象

web瀏覽器中常見的二進制對象有Blob和File等。

Blob

Blob對象表明一段二進制對象,並提供了一系列對該二進制數據操做的方法。 生成Blob對象可使用Blob構造函數,或者對已有Blob對象使用slice切片生成。

const obj = {hello: 'world'};
const blob = new Blob([JSON.stringify(obj)], {type: 'application/json'});
複製代碼

Blob構造函數接受兩個參數,一個是buffer數據數組,第二個是隻有一個type屬性的options對象,type指定文件的MIME類型。

生成的Blob實例有兩個只讀的屬性。

size 生成二進制數據的大小,單位是字節 type 生成二進制數據的MIME類型

File

File對象繼承與Blob對象,在Blob的基礎上增長了一些屬性。

File.prototype instanceof Blob  // true
複製代碼

最多見的File類型來自type爲file的input元素。

<input type="file" id="uploader" />

<script> const uploader = document.getElementById('uploader'); uploader.addEventListener('change', () => { const file = uploader.files[0]; // file // { // lastModified: xxxxxxxxxxx, // lastModifiedDate: Thu Apr 11 2019 10:52:35 GMT+0800 (CST), // name: "avatar.png", // size: 7040, // type: "image/png" // } }); </script>
複製代碼

FileReader

在獲取到Blob對象或File對象後,咱們一般還須要藉助FileReader對象將咱們的二進制數據轉換成須要的形式,如轉換成DataURL後用於image, audio, video等標籤的直接引入,或轉化成ArrayBuffer後進一步對咱們的二進制數據進行操做。

FileReader提供瞭如下轉化方法。

  • FileReader.prototype.readAsDataURL
  • FileReader.prototype.readAsArrayBuffer
  • FileReader.prototype.readAsText
  • FileReader.prototype.readAsBinaryString
function readBlob(blob, type) {
    return new Promise(resolve => {
        const reader = new FileReader();
        reader.onload = (e) => {
            resolve(e.target.result);
        };
        reader[`readAs${type}`](blob);
    });
}

const blob = new Blob('hello, world!'.split(''));
readBlob(blob, 'DataURL').then(console.log);
複製代碼

其餘

除了FileReader外,還有些web api能夠輸出咱們的Blob/File數據。

  • URL.createURLObject
  • Response對象提供的方法
    • Response.prototype.arrayBuffer
    • Response.prototype.blob
    • Response.prototype.json
    • Response.prototype.text
    • Response.prototype.formData
const str = 'hello, world';
const blob = new Blob(str.split(''));

URL.createURLObject(blob);

new Response(blob).arrayBuffer(buffer => console.log(new Int8Array(buffer));
// Response也能夠直接轉化一個String
new Response(str).arrayBuffer(buffer => console.log(new Int8Array(buffer));
複製代碼

應用

根據以上的內容,咱們已經能夠進行String, Blob/File, ArrayBuffer, TypedArray, DataURL, ObjectURL這些對象之間的相互轉化了。有的轉化操做直接使用現有api便可,有的須要中間狀態過分。如下爲幾種常見的應用到這些轉換的場景。

將json數據下載爲本地文件。

function download (url, name) {
  const a = document.createElement('a')
  a.download = name
  a.rel = 'noopener'
  a.href = url
  a.dispatchEvent(new MouseEvent('click'))   // or a.click()
}
// 要被下載的json數據
const json = {
    name: 'coffeeBean'
};
const str = JSON.stringify(json, null, 4);
const dataURL = `data;,${str}`;
const blob = new Blob(str.split(''));
const url = URL.createObjectURL(blob);

download(dataURL, 'demo1.json');
download(url, 'demo2.json');
複製代碼

上傳本地圖片並在網頁中顯示。

<input type="file" id="uploader" />

<script> const uploader = document.getElementById('uploader'); uploader.addEventListener('change', () => { const file = uploader.files[0]; // readBlob函數在以上FileReader小節定義 readBlob(file, 'DataURL').then(result => { const img = new Image(); img.src = result; document.body.append(img); }); }); </script>
複製代碼

利用canvas實現圖片壓縮

function compress(img, mimeType, maxWidth, quality) {
    const canvas = document.createElement('canvas');
    cosnt ctx = canvas.getContext('2d');
    let width = img.width;
    let height = img.height;
    if (width > maxWidth) {
        height = Math.round(height *= maxWidth / width);
        width = maxWidth;
    }
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(img, 0, 0, width, height);
    return new Promise(resolve => {
        canvas.toBlob(blob => {
            resolve(blob);
        }, mimeType, quality);
    });
}

// compress(img, 'image/png', 1080, 0.9)
// 第一個參數爲要壓縮的圖片對象
// 第二個爲圖片的MIME類型
// 第三個爲圖片等比壓縮後的最大寬度
// 第四個參數爲canvas壓縮的質量
// 函數返回壓縮後的圖片Blob,能夠做爲圖片文件上傳到服務器
複製代碼

js中二進制數據類型的應用場景還有不少,但理解了這些二進制對象之間的關係以及原理以後,就可以在各類二進制數據中自由轉換,場景應對起來也就遊刃有餘了。

參考

相關文章
相關標籤/搜索