在 HTML 5 中,对 XMLHttpRequest(后续都使用 XHR 表示) 新增了关于跨域请求、进度事件的上传、二进制数据的上传及下载等许多新的功能。
html5 中新增了 Fetch API,与 XHR 的主要区别在于 Fetch API 使用了 Promise。

学习目标:

  • 掌握 XHR 新增的 responseType 属性与 response 属性的概念、作用及使用方法;
  • 掌握 XHR 可以发送哪些类型的数据以及如何发送这些数据;
  • 掌握如何使用 XHR 跨域请求数据;
  • 掌握 Fetch API 的基本概念及核心方法的使用,以及如何使用 fetch 方法向服务器端发送数据及解析响应;

从服务器端获取二进制数据

在 HTML5 之前,当使用 XHR 对象从服务器端获取二进制数据时,需要通过 XHR 对象的 overrideMimeType 方法来重载所获取数据的 Mime Type 类型,将所获取数据的字符编码(charset)修改为用户自定义类型。代码如下所示。

  1. var xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'test.png', true);
  3. xhr.overrideMimeType('test/plain; charset=x-user-defined');
  4. xhr.onreadystatechange = function(e) {
  5. if(this.readyState == 4 && this.status == 200) {
  6. var binStr = this.responseText;
  7. for(var i=0, len=binStr.length; i<len; ++i) {
  8. var c = binStr.charCodeAt(i);
  9. var byte = c & 0xff;
  10. }
  11. }
  12. }

虽然通过这种方法也能获取二进制数据,但是 XHR 对象的 responseText 属性值返回的并不是原始二进制数据,而是由这些数据所组成的一串字符串。

在 HTML5 中,不推荐使用这种通过重载 MIME Type 类型来自定义数据的字符编码的方法。为了解决此类问题,HTML5 为 XHR t对象新增 responseType 属性与 response 属性。说明如下。

  • responseText 属性:指定服务器端返回数据的数据类型,默认是 text。可指定属性值为 text、arraybuffer、blob、json 或 document。
  • response 属性:如果发送请求成功,则 XHR 对象的 reaponse 属性值返回服务器端响应的数据。
    • 当 responseType 为 text 时,response 属性值是一串字符串;
    • 当 responseType 为 arraybuffer 时,response 属性值是一个 ArrayBuffer 对象;
    • 当 responseType 为 blob 时,response 属性值是一个 Blob 对象;
    • 当 responseType 为 json 时,response 属性值是一个 Json 对象;
    • 当 responseType 为 document 时,response 属性值是一个 Document 对象。

到目前,Firefox 8以上、Opera 11.64以上、Chrome 10以上、Safari 5以上以及IE 10版本的浏览器支持 XHR 对象的 response 属性值指定为 arraybuffer 进行使用。

ArrayBuffer响应

下面是一个指定 XHR 对象的 response 属性值为 arraybuffer 的例子。示例中,当点击“下载图片”将下载一张图片,并且结合前面的本地存储知识,将下载的图片存储到 indexedDB 中;当点击“显示图片”将从 indexedDB 中取出图片显示。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document</title>
  8. </head>
  9. <body onload="init();">
  10. <div>
  11. <p>将responseType属性值指定为“arraybuffer”</p>
  12. <input type="button" value="下载图片" onclick="downloadPic();" />
  13. <input type="button" value="显示图片" onclick="showPic();" />
  14. <output id="result"></output>
  15. </div>
  16. </body>
  17. </html>
  18. <script>
  19. window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
  20. window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
  21. window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
  22. window.IDBCursor = window.IDBCursor || window.webkitIDBCursor || window.msIDBCursor;
  23. window.URL = window.URL || window.webkitURL;
  24. var dbName = 'imgBD';
  25. var dbVersion = 20150318;
  26. var idb;
  27. function init() {
  28. var dbConnect = indexedDB.open(dbName, dbVersion);
  29. dbConnect.onsuccess = function(e) {
  30. idb = e.target.result;
  31. console.log('数据库连接成功');
  32. };
  33. dbConnect.onerror = function() {
  34. console.log('数据库连接失败');
  35. };
  36. dbConnect.onupgradeneeded = function(e) {
  37. idb = e.target.result;
  38. var tx = e.target.transaction;
  39. tx.onabort = function(e) {
  40. console.log('对象仓库创建失败');
  41. }
  42. var tableName = 'img';
  43. var tableParams = {
  44. keyPath: 'id',
  45. autoIncrement: true
  46. }
  47. var store = idb.createObjectStore(tableName, tableParams);
  48. console.log('对象仓库创建成功');
  49. }
  50. }
  51. function downloadPic() {
  52. var xhr = new XMLHttpRequest();
  53. xhr.open('GET', './12.png', true);
  54. xhr.responseText = 'arraybuffer';
  55. xhr.onload = function(e) {
  56. if(this.status === 200) {
  57. var bb = new Blob([this.response]);
  58. var reader = new FileReader();
  59. reader.readAsDataURL(bb);
  60. reader.onload = function() {
  61. // 在IndexedDB数据库中保存二进制数据
  62. var tx = idb.transaction(['img'], 'readwrite');
  63. tx.oncomplete = function() {
  64. console.log('数据保存成功');
  65. }
  66. tx.onabort = function() {
  67. console.log('数据保存失败');
  68. }
  69. var store = tx.objectStore('img');
  70. var value = {
  71. id: 1,
  72. img: this.result
  73. }
  74. store.put(value);
  75. }
  76. }
  77. };
  78. xhr.send();
  79. }
  80. function showPic() {
  81. var tx = idb.transaction(['img'], 'readonly');
  82. var store = tx.objectStore('img');
  83. var req = store.get(1);
  84. req.onsuccess = function() {
  85. if(this.result == undefined) {
  86. console.log('没有符合条件的数据');
  87. } else {
  88. var img = document.createElement('img');
  89. img.src = this.result.img;
  90. img.width = 200;
  91. img.height = 200;
  92. document.body.append(img);
  93. }
  94. }
  95. req.onerror = function() {
  96. console.log('获取数据失败');
  97. }
  98. }
  99. </script>

Blob响应

也可以将 XHR 对象的 responseType 属性值指定为“blob”,从而将服务器端下载的二进制数据用作一个 Blob 对象。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body onload="init();">
  <div>
    <p>将responseType属性值指定为“arraybuffer”</p>
    <input type="button" value="下载图片" onclick="downloadPic();" />
    <input type="button" value="显示图片" onclick="showPic();" />
    <output id="result"></output>
  </div>
</body>
</html>
<script>

window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
window.IDBCursor = window.IDBCursor || window.webkitIDBCursor || window.msIDBCursor;
window.URL = window.URL || window.webkitURL;

var dbName = 'imgBD';
var dbVersion = 20150318;
var idb;

function init() {
  var dbConnect = indexedDB.open(dbName, dbVersion);
  dbConnect.onsuccess = function(e) {
    idb = e.target.result;
    console.log('数据库连接成功');
  };

  dbConnect.onerror = function() {
    console.log('数据库连接失败');
  };

  dbConnect.onupgradeneeded = function(e) {
    idb = e.target.result;
    var tx = e.target.transaction;
    tx.onabort = function(e) {
      console.log('对象仓库创建失败');
    }

    var tableName = 'img';
    var tableParams = {
      keyPath: 'id',
      autoIncrement: true
    }
    var store = idb.createObjectStore(tableName, tableParams);
    console.log('对象仓库创建成功');
  }
}

function downloadPic() {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', './12.png', true);
  xhr.responseText = 'blob';
  xhr.onload = function(e) {
    if(this.status === 200) {
      reader.readAsDataURL(this.response);
      reader.onload = function(f) {
        // 在IndexedDB数据库中保存二进制数据
        var tx = idb.transaction(['img'], 'readwrite');
        tx.oncomplete = function() {
          console.log('数据保存成功');
        }

        tx.onabort = function() {
          console.log('数据保存失败');
        }

        var store = tx.objectStore('img');
        var value = {
          id: 1,
          img: this.result
        }
        store.put(value);
      }
    }
  };
  xhr.send();
}

function showPic() {
  var tx = idb.transaction(['img'], 'readonly');
  var store = tx.objectStore('img');
  var req = store.get(1);

  req.onsuccess = function() {
    if(this.result == undefined) {
      console.log('没有符合条件的数据');
    } else {
      var img = document.createElement('img');
      img.src = this.result.img;
      img.width = 200;
      img.height = 200;
      document.body.append(img);
    }
  }

  req.onerror = function() {
    console.log('获取数据失败');
  }
}
</script>

发送数据

html5以前,XHR 对象的 send 方法只能发送字符串或 Document 对象,而在 HTML5 中,对 XHR 对象的 send 方法进行改善,使其可以发送字符串、Document 对象、表单数据、Blob 对象、文件以及 ArrayBuffer 对象。

发送字符串

通过将 XMLHttpRequest 对象的 responseType 属性值指定为 text。

发送表单数据

上传文件

发送Blob对象

发送ArrayBuffer对象