使用Blob進行文件上傳

Blob,Binary Large Object的縮寫,二進制類型的大對象,表明不可改變的原始數據json

Blob基本用法

Blob對象

Blob對象指的是字節序列,而且具備size屬性,是字節序列中的字節總數,和一個type屬性,它是小寫的ASCII編碼的字符串表示的媒體類型字節序列。數組

size:以字節數返回字節序列的大小。獲取時,符合要求的用戶代理必須返回一個FileReader或一個FileReaderSync對象能夠讀取的總字節數,若是Blob沒有要讀取的字節,則返回0 。
type:小寫的ASCII編碼字符串表示媒體類型Blob。在獲取時,用戶代理必須Blob以小寫形式返回a類型的ASCII編碼字符串,這樣當它轉換爲字節序列時,它是可解析的MIME類型,或者是空字符串(0字節)若是是類型沒法肯定。

構造函數

建立blob對象本質上和建立一個其餘對象的方式是同樣的,都是使用Blob() 的構造函數來進行建立。 構造函數接受兩個參數:瀏覽器

第一個參數爲一個數據序列,格式能夠是 ArrayBuffer, ArrayBufferView, Blob, DOMString
第二個參數是一個包含如下兩個屬性的對象
  • type: MIME的類型,
  • endings: 決定第一個參數的數據格式。默認值爲"transparent",用於指定包含行結束符n的字符串如何被寫入。 它是如下兩個值中的一個: "native",表示行結束符會被更改成適合宿主操做系統文件系統的換行符; "transparent",表示會保持blob中保存的結束符不變。
var data1 = "a";
    var blob1 = new Blob([data1]);
    console.log(blob1);  //輸出:Blob {size: 1, type: ""}
    
    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"}
    
    // 建立一個8字節的ArrayBuffer,在其上建立一個每一個數組元素爲2字節的「視圖」
    var abf = new ArrayBuffer(8)
    var abv = new Int16Array(abf)
    var bolb_ArrayBuffer = new Blob(abv, {type : 'text/plain'})
    console.log(bolb_ArrayBuffer)      //輸出 Blob(4) {size: 4, type: "text/plain"}

slice方法

Blob對象有一個slice方法,返回一個新的 Blob對象,包含了源 Blob對象中指定範圍內的數據。網絡

slice(start, end, contentType)
start: 可選,表明 Blob 裏的下標,表示第一個會被會被拷貝進新的 Blob 的字節的起始位置。若是傳入的是一個負數,那麼這個偏移量將會從數據的末尾從後到前開始計算。
end: 可選,表明的是 Blob 的一個下標,這個下標-1的對應的字節將會是被拷貝進新的Blob 的最後一個字節。若是你傳入了一個負數,那麼這個偏移量將會從數據的末尾從後到前開始計算。
contentType: 可選,給新的 Blob 賦予一個新的文檔類型。這將會把它的 type 屬性設爲被傳入的值。它的默認值是一個空的字符串。
var data = "abcdef";
var blob1 = new Blob([data]);
var blob2 = blob1.slice(0,3);

console.log(blob1);  //輸出:Blob {size: 6, type: ""}
console.log(blob2);  //輸出:Blob {size: 3, type: ""}

slice用於文件分片上傳

  • 分片與併發結合,將一個大文件分割成多塊,併發上傳,極大地提升大文件的上傳速度。
  • 當網絡問題致使傳輸錯誤時,只須要重傳出錯分片,而不是整個文件。另外分片傳輸可以更加實時的跟蹤上傳進度。

分片上傳邏輯以下:併發

  • 獲取要上傳文件的File對象,根據chunk(每片大小)對文件進行分片
  • 經過post方法輪循上傳每片文件,其中url中拼接querystring用於描述當前上傳的文件信息;post body中存放本次要上傳的二進制數據片斷
  • 接口每次返回offset,用於執行下次上傳
initUpload();

//初始化上傳
function initUpload() {
    var chunk = 100 * 1024;   //每片大小
    var input = document.getElementById("file");    //input file
    input.onchange = function (e) {
        var file = this.files[0];
        var query = {};
        var chunks = [];
        if (!!file) {
            var start = 0;
            //文件分片
            for (var i = 0; i < Math.ceil(file.size / chunk); i++) {
                var end = start + chunk;
                chunks[i] = file.slice(start , end);
                start = end;
            }
            
            // 採用post方法上傳文件
            // url query上拼接如下參數,用於記錄上傳偏移
            // post body中存放本次要上傳的二進制數據
            query = {
                fileSize: file.size,
                dataSize: chunk,
                nextOffset: 0
            }

            upload(chunks, query, successPerUpload);
        }
    }
}

// 執行上傳
function upload(chunks, query, cb) {
    var queryStr = Object.getOwnPropertyNames(query).map(key => {
        return key + "=" + query[key];
    }).join("&");
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://xxxx/opload?" + queryStr);
    xhr.overrideMimeType("application/octet-stream");
    
    //獲取post body中二進制數據
    var index = Math.floor(query.nextOffset / query.dataSize);
    getFileBinary(chunks[index], function (binary) {
        if (xhr.sendAsBinary) {
            xhr.sendAsBinary(binary);
        } else {
            xhr.send(binary);
        }

    });

    xhr.onreadystatechange = function (e) {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                var resp = JSON.parse(xhr.responseText);
                // 接口返回nextoffset
                // resp = {
                //     isFinish:false,
                //     offset:100*1024
                // }
                if (typeof cb === "function") {
                    cb.call(this, resp, chunks, query)
                }
            }
        }
    }
}

// 每片上傳成功後執行
function successPerUpload(resp, chunks, query) {
    if (resp.isFinish === true) {
        alert("上傳成功");
    } else {
        //未上傳完畢
        query.offset = resp.offset;
        upload(chunks, query, successPerUpload);
    }
}

// 獲取文件二進制數據
function getFileBinary(file, cb) {
    var reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = function (e) {
        if (typeof cb === "function") {
            cb.call(this, this.result);
        }
    }
}

Blob URL

blob協議的url使用時就像平時使用的url同樣,能夠做爲圖片請求地址,也能夠做爲文件請求地址。格式:app

blob: http://XXX
  • URL.createObjectURL(blob) 建立連接
  • URL.revokeObjectURL(url)

下面是一個下載文件的示例,直接調用便可實現文件下載ide

// file是要下載的文件(blob對象)
downloadHandler: function (file, fileName) {
  let link = document.createElement('a')
  link.href = window.URL.createObjectURL(file)
  link.download = fileName
  link.click()
  window.URL.revokeObjectURL(link.href)
  if (navigator.userAgent.indexOf('Firefox') > -1) {
    const a = document.createElement('a')
    a.addEventListener('click', function (e) {
      a.download = fileName
      a.href = URL.createObjectURL(file)
    })
    let e = document.createEvent('MouseEvents')
    e.initEvent('click', false, false)
    a.dispatchEvent(e)
  }
}

在從後臺獲取的數據接口中把返回類型設置爲blob函數

var x = new XMLHttpRequest();
x.responseType = 'blob';       // 返回一個blob對象

Blob URL和Data URL的區別

Blob URL
blob URL
Data URL
data URLpost

  • Blob URL的長度通常比較短,但Data URL由於直接存儲圖片base64編碼後的數據,每每很長,如上圖所示,瀏覽器在顯示Data URL時使用了省略號(…)。當顯式大圖片時,使用Blob URL能獲取更好的可能性。
  • Blob URL能夠方便的使用XMLHttpRequest獲取源數據,好比設置XMLHttpRequest返回的數據類型爲blob
  • Blob URL 只能在當前應用內部使用,把Blob URL複製到瀏覽器的地址欄中,是沒法獲取數據的。Data URL相比之下,就有很好的移植性,能夠在任意瀏覽器中使用。
相關文章
相關標籤/搜索