詳細解讀XMLHttpRequest(二)響應屬性、二進制數據、監測上傳下載進度

本文主要參考:MDN數組

分析並操做 responseXML屬性瀏覽器

若是你使用 XMLHttpRequest 來得到一個遠程的 XML 文檔的內容,responseXML 屬性將會是一個由 XML 文檔解析而來的 DOM 對象,這很難被操做和分析。這裏有五種主要的分析 XML 文檔的方式:服務器

  1. 使用 XPath 定位到文檔的制定部分。
  2. 使用 JXON 將其轉換成 JavaScript 對象樹。
  3. 手工的 解析和序列化 XML 爲字符串或對象。
  4. 使用 XMLSerializer 把 DOM 樹序列化成字符串或文件。
  5. 若是你預先知道 XML 文檔的內容,你能夠使用 RegExp。若是你用 RegExp 掃描時受到換行符的影響,你也許想要刪除全部的換行符。然而,這種方法是"最後手段",由於若是 XML 代碼發生輕微變化,該方法將可能失敗。

解析和操做包含 HTML 文檔的 responseText 屬性網絡

注意: 在 W3C XMLHttpRequest 規範中容許 HTML 經過 XMLHttpRequest.responseXML 屬性進行解析。更多詳細內容請閱讀 HTML in XMLHttpRequest 。
若是使用 XMLHttpRequest 從遠端獲取一個 HTML 頁面,則全部 HTML 標記會以字符串的形式存放在responseText 屬性裏,這樣就使得操做和解析這些標記變得困難。解析這些HTML標記主要有三種方式:併發

  1. 使用 XMLHttpRequest.responseXML 屬性。
  2. 將內容經過 fragment.body.innerHTML 注入到一個 文檔片斷 中,並遍歷 DOM 中的片斷。
  3. 若是你預先知道 HTML 文檔的內容,你能夠使用 RegExp 。若是你用 RegExp 掃描時受到換行符的影響,你也許想要刪除全部的換行符。 然而,這種方法是"最後手段",由於若是 HTML 代碼發生輕微變化,該方法將可能失敗。

Handling binary dataapp

儘管 XMLHttpRequest 通常用來發送和接收文本數據,但其實也能夠發送和接受二進制內容。有許多通過良好測試的方法來強制使用 XMLHttpRequest 發送二進制數據。利用 XMLHttpRequest 的 .overrideMimeType() 方法是一個解決方案,雖然它並非一個標準方法。ide

var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
// retrieve data unprocessed as a binary string
oReq.overrideMimeType("text/plain; charset=x-user-defined");
/* ... */

在 XMLHttpRequest Level 2 規範中新加入了 responseType 屬性 ,使得發送和接收二進制數據變得更加容易。函數

var oReq = new XMLHttpRequest();
oReq.onload = function(e) {
  var arraybuffer = xhr.response; // not responseText
  /* ... */
}
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.send();

使用JavaScript類型數組接受二進制數據測試

能夠經過設置一個XMLHttpRequest對象的responseType屬性來改變一個從服務器上返回的響應的數據類型.可用的屬性值爲空字符串 (默認), "arraybuffer", "blob", "document", 和 "text". response屬性的值會根據responseType屬性的值的不一樣而不一樣, 可能會是一個 ArrayBuffer, Blob, Document, string,或者爲NULL(若是請求未完成或失敗)
下例讀取了一個二進制圖像文件,而且由該文件的二進制原生字節建立了一個8位無符號整數的數組.ui

var oReq = new XMLHttpRequest();
oReq.open("GET", "/myfile.png", true);
oReq.responseType = "arraybuffer";
oReq.onload = function (oEvent) {
  var arrayBuffer = oReq.response; // 注意:不是oReq.responseText
  if (arrayBuffer) {
    var byteArray = new Uint8Array(arrayBuffer);
    for (var i = 0; i < byteArray.byteLength; i++) {
      // 對數組中的每一個字節進行操做
    }
  }
};
oReq.send(null);

除了上面的方法,還能夠使用 BlobBuilder API 直接將arraybuffer數據添加進一個Blob對象中, 因爲該API還在試驗階段,因此須要加上特定的前綴:

var BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.MSBlobBuilder || window.BlobBuilder;
var oReq = new XMLHttpRequest();
oReq.open("GET", "/myfile.png", true);
oReq.responseType = "arraybuffer";
oReq.onload = function(oEvent) {
  var blobBuilder = new BlobBuilder();
  blobBuilder.append(oReq.response);
  var blob = blobBuilder.getBlob("image/png");
  // ...
};
oReq.send();

在老的瀏覽器中接受二進制數據

下面的load_binary_resource()方法能夠從指定的URL那裏加載二進制數據,並將數據返回給調用者.

function load_binary_resource(url) {
  var req = new XMLHttpRequest();
  req.open(\'GET\', url, false);
  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
  req.overrideMimeType(\'text/plain; charset=x-user-defined\');
  req.send(null);
  if (req.status != 200) return \'\';
  return req.responseText;
}

最爲奇妙的操做在第五行,該行重寫了默認的MIME類型,強制瀏覽器將該響應當成純文本文件來對待, 使用一個用戶自定義的字符集.這樣就是告訴了瀏覽器,不要去解析數據,直接返回未處理過的字節碼.

var filestream = load_binary_resource(url);
var abyte = filestream.charCodeAt(x) & 0xff; // 扔掉的高位字節(f7)

上例從請求回來的二進制數據中獲得偏移量爲x處的字節.有效的偏移量範圍是0到filestream.length-1.
查看 使用XMLHttpRequest下載文件 瞭解詳情,查看下載文件.
發送二進制數據
XMLHttpRequest對象的send方法已被加強,能夠經過簡單的傳入一個ArrayBuffer, Blob, 或者 File對象來發送二進制數據.
下例建立了一個文本文件,並使用POST方法將該文件發送到了服務器上.你也能夠使用文本文件以外的其餘二進制數據類型.

var oReq = new XMLHttpRequest();
oReq.open("POST", url, true);
oReq.onload = function (oEvent) {
  // 上傳完成後.
};
var bb = new BlobBuilder(); // 須要合適的前綴: window.MozBlobBuilder 或者 window.WebKitBlobBuilder
bb.append(\'abc123\');
oReq.send(bb.getBlob(\'text/plain\'));

將類型數組做爲二進制數據發送
你能夠將JavaScript類型數組做爲二進制數據發送出去.

var myArray = new ArrayBuffer(512);
var longInt8View = new Uint8Array(myArray);
for (var i=0; i< longInt8View.length; i++) {
  longInt8View[i] = i % 255;
}
var xhr = new XMLHttpRequest;
xhr.open("POST", url, false);
xhr.send(myArray);

上例新建了一個512字節的8位整數數組併發送它,固然,你也能夠發送任意的二進制數據
監測進度
支持 DOM 的 progress 事件監測之於 XMLHttpRequest 傳輸,遵循 Web API 進度事件規範 : 這些事件實現了 ProgressEvent 接口。

var req = new XMLHttpRequest();
//上傳監聽
req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.addEventListener("error", transferFailed, false);
req.addEventListener("abort", transferCanceled, false);

req.open(...);
...
// progress on transfers from the server to the client (downloads)
function updateProgress(evt) {
  if (evt.lengthComputable) {
    var percentComplete = evt.loaded / evt.total;
    ...
  } else {
    // Unable to compute progress information since the total size is unknown
  }
}

注意: 你須要在請求調用 open() 以前添加事件監聽。不然 progress 事件將不會被觸發。
在上個例子中,progress 事件被指定由 updateProgress() 函數處理,並接收到傳輸的總字節數 total 和已經傳輸的字節數loaded ,total是自「Content-Length」頭傳輸的數據的總體長度(字節)。可是若是 lengthComputable 屬性的值是 false,那麼總字節數是未知而且 total 的值爲0,若知道長度則lengthComputable屬性爲true
progress 事件同時存在於下載和上傳的傳輸。下載相關事件在 XMLHttpRequest 對象上被觸發,就像上面的例子同樣。上傳相關事件在 XMLHttpRequest.upload 對象上被觸發,像下面這樣:

var req = new XMLHttpRequest();
//下載監聽
req.upload.addEventListener("progress", updateProgress);
req.upload.addEventListener("load", transferComplete);
req.upload.addEventListener("error", transferFailed);
req.upload.addEventListener("abort", transferCanceled);
req.open();

注意:progress 事件在使用 file: 協議的狀況下是無效的。
使用 loadend 事件能夠偵測到全部的三種加載結束條件(abort、load、error):
req.addEventListener("loadend", loadEnd, false);
須要注意的是,沒有方法能夠確切的知道 loadend 事件接收到的信息是來自何種條件引發的操做終止;可是你能夠在全部傳輸結束的時候使用這個事件處理。

XMLHttpRequest對象在請求的不一樣階段觸發不一樣類型的事件,因此它不須要檢查readyState屬性。
當調用send()時,觸發單個loadstart事件。當正在加載服務器的響應時,XMLHttpRequest對象會發生progress事件,一般每隔50毫秒左右,因此能夠使用這些事件給用戶反饋請求的進度。
若是請求快速完成,它可能從不會觸發progress事件。當事件完成,會觸發load事件。
HTTP請求沒法完成有3種狀況,對應3種事件。若是請求超時,會觸發timeout事件。若是請求停止,會觸發abort事件。像太多重定向這樣的網絡錯誤會阻止請求完成,但這些狀況發生時會觸發error事件。
對於任何具體請求,瀏覽器將只會觸發load、abort、timeout和error事件中的一個,還有progress事件。

if(\'onprogress\' in (new XMLHttpRequest())){ //檢測是否支持progress事件
    var request = new XMLHttpRequest();
    request.onprogress = function (e) {
        if(e.lengthComputable){
            progress.innerHTML = Math.round(100* e.loaded/ e.total) + \'%\';
        }
    }
}
相關文章
相關標籤/搜索