• XMLHttpRequest2的进步之处


    本文参考自:XMLHttpRequest2 新技巧 (重点保留demo,方便自己日后查阅)

    HTML5是现在web开发中的热点,虽然关于web app和local app一直有争论,但是从技术学习的角度,html5技术无疑是值得学习的。最近看了看XHR2,大概了解了其中比之前进步的要点,记录下来以备日后复习:

    首先,XHR2的官方注解可见:http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html

    XHR2主要的新功能有(我平时开发遇到的):

    1. 上传下载二进制数据

    2. 上传进度事件的支持

    3. 跨域请求

    同时结合html5中的File API,我们就可以在网页中实现更丰富的功能。

    一) 二进制数据处理

    以前通过 XHR 抓取二进制 blob 形式的文件是很痛苦的事情。从技术上来说,这甚至是不可能的实现。有一种广为流传的一种技巧,是将 MIME 类型替换为由用户定义的字符集,如下所示:

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

    虽然这种方法可行,但是 responseText 中实际返回的并不是二进制 blob,而是代表图片文件的二进制字符串。我们要巧妙地让服务器在不作处理的情况下,将这些数据传递回去。

    现在XHR2中,新增了responseType和response属性,可以告知浏览器我们希望返回什么格式的数据。

    1. xhr.responseType
      在发送请求前,根据您的数据需要,将 xhr.responseType 设置为“text”、“arraybuffer”、“blob”或“document”。请注意,设置(或忽略)xhr.responseType = '' 会默认将响应设为“text”。

    2. xhr.response

      成功发送请求后,xhr 的响应属性会包含 DOMString、ArrayBuffer、Blob 或 Document 形式(具体取决于 responseTyp  的设置)的请求数据。

    凭借这个优秀的新属性,我们可以修改上一个示例:以 ArrayBuffer 而非字符串的形式抓取图片。将缓冲区移交给 BlobBuilder API 可创建 Blob: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
      
    var xhr = new XMLHttpRequest();
    xhr.open('GET''/path/to/image.png'true);
    xhr.responseType = 'arraybuffer';
      
    xhr.onload = function(e) {
      if (this.status == 200) {
        var bb = new BlobBuilder();
        bb.append(this.response); // Note: not xhr.responseText
      
        var blob = bb.getBlob('image/png');
        ...
      }
    };
      
    xhr.send();

    此外ArrayBuffer是二进制数据通用的固定长度容器。如果您需要原始数据的通用缓冲区,ArrayBuffer 就非常好用,但是它真正强大的功能是让您使用 JavaScript 类型数组创建底层数据的“视图”。实际上,可以通过单个 ArrayBuffer 来源创建多个视图。例如,您可以创建一个 8 位整数数组,与来自相同数据的现有 32 位整数数组共享同一个 ArrayBuffer。底层数据保持不变,我们只是创建其不同的表示方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var xhr = new XMLHttpRequest();
    xhr.open('GET''/path/to/image.png'true);
    xhr.responseType = 'arraybuffer';
      
    xhr.onload = function(e) {
      var uInt8Array = new Uint8Array(this.response); // this.response == uInt8Array.buffer
      // var byte3 = uInt8Array[4]; // byte at offset 4
      ...
    };
      
    xhr.send();

    如果您要直接处理 Blob 且/或不需要操作任何文件的字节,可使用xhr.responseType='blob'

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    window.URL = window.URL || window.webkitURL;  // Take care of vendor prefixes.
      
    var xhr = new XMLHttpRequest();
    xhr.open('GET''/path/to/image.png'true);
    xhr.responseType = 'blob';
      
    xhr.onload = function(e) {
      if (this.status == 200) {
        var blob = this.response;
      
        var img = document.createElement('img');
        img.onload = function(e) {
          window.URL.revokeObjectURL(img.src); // Clean up after yourself.
        };
        img.src = window.URL.createObjectURL(blob);
        document.body.appendChild(img);
        ...
      }
    };
      
    xhr.send();

    Blob 可用于很多场合,包括保存到 indexedDB、写入 HTML5 文件系统 或创建 Blob 网址(如本例中所示)。

    二)发送数据

    能够下载各种格式的数据固然是件好事,但是如果不能将这些丰富格式的数据送回本垒(服务器),那就毫无意义了。XMLHttpRequest 有时候会限制我们发送 DOMString 或 Document (XML) 数据。但是现在不会了。现已替换成经过修改的 send() 方法,可接受以下任何类型:DOMString、Document、FormData、Blob、File、ArrayBuffer。本部分的其余内容中的示例演示了如何使用各类型发送数据。

    1)发送字符串数据:xhr.send(DOMString)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function sendText(txt) {
      var xhr = new XMLHttpRequest();
      xhr.open('POST''/server'true);
      xhr.responseType ='text';  xhr.onload = function(e) {
        if (this.status == 200) {
          console.log(this.responseText);
        }
      };
      
      xhr.send(txt);
    }
      
    sendText('test string');

    2)提交表单:xhr.send(FormData)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function sendForm() {
      var formData = new FormData();
      formData.append('username''johndoe');
      formData.append('id', 123456);
      
      var xhr = new XMLHttpRequest();
      xhr.open('POST''/server'true);
      xhr.onload = function(e) { ... };
      
      xhr.send(formData);
    }

    由form数据初始化formData

    1
    2
    3
    4
    5
    <form id="myform" name="myform" action="/server">
      <input type="text" name="username" value="johndoe">
      <input type="number" name="id" value="123456">
      <input type="submit" onclick="return sendForm(this.form);">
    </form>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function sendForm(form) {
      var formData = new FormData(form);
      
      formData.append('secret_token''1234567890'); // Append extra data before send.
      
      var xhr = new XMLHttpRequest();
      xhr.open('POST', form.action, true);
      xhr.onload = function(e) { ... };
      
      xhr.send(formData);
      
      return false// Prevent page from submitting.
    }

    同时可以包含文件上传

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function uploadFiles(url, files) {
      var formData = new FormData();
      
      for (var i = 0, file; file = files[i]; ++i) {
        formData.append(file.name, file);
      }
      
      var xhr = new XMLHttpRequest();
      xhr.open('POST', url, true);
      xhr.onload = function(e) { ... };
      
      xhr.send(formData);  // multipart/form-data
    }
      
    document.querySelector('input[type="file"]').addEventListener('change',function(e) {
      uploadFiles('/server'this.files);
    }, false);

    3)上传文件或 blob:xhr.send(Blob),同时demo下上传事件如果使用

    1
    <progress min="0" max="100" value="0">0% complete</progress>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    function upload(blobOrFile) {
      var xhr = new XMLHttpRequest();
      xhr.open('POST''/server'true);
      xhr.onload = function(e) { ... };
      
      // Listen to the upload progress.
      var progressBar = document.querySelector('progress');
      xhr.upload.onprogress = function(e) {
        if (e.lengthComputable) {
          progressBar.value = (e.loaded / e.total) * 100;
          progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
        }
      };
      
      xhr.send(blobOrFile);
    }
      
    // Take care of vendor prefixes.
    BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
      
    var bb = new BlobBuilder();
    bb.append('hello world');
      
    upload(bb.getBlob('text/plain'));

    4)上传字节:xhr.send(ArrayBuffer)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function sendArrayBuffer() {
      var xhr = new XMLHttpRequest();
      xhr.open('POST''/server'true);
      xhr.onload = function(e) { ... };
      
      var uInt8Array = new Uint8Array([1, 2, 3]);
      
      xhr.send(uInt8Array.buffer);
    }

    三)跨源请求 (CORS)

    CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX 请求。启用此功能非常简单,只需由服务器发送一个响应标头即可

    允许来自 example.com 的请求:

    1
    Access-Control-Allow-Origin: http://example.com

    要允许任何域向您提交请求:

    1
    Access-Control-Allow-Origin: *

    提交跨域请求时和以前没有区别。

    1
    2
    3
    4
    5
    6
    7
    var xhr = new XMLHttpRequest();
    xhr.onload = function(e) {
      var data = JSON.parse(this.response);
      ...
    }
    xhr.send();

    四)有用的实例

    1)下载文件并保存到文件系统

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;
      
    function onError(e) {
      console.log('Error', e);
    }
      
    var xhr = new XMLHttpRequest();
    xhr.open('GET''/path/to/image.png'true);
    xhr.responseType = 'arraybuffer';
      
    xhr.onload = function(e) {
      
      window.requestFileSystem(TEMPORARY, 1024 * 1024, function(fs) {
        fs.root.getFile('image.png', {create: true},function(fileEntry) {
          fileEntry.createWriter(function(writer) {
      
            writer.onwrite = function(e) { ... };
            writer.onerror = function(e) { ... };
      
            var bb = new BlobBuilder();
            bb.append(xhr.response);
      
            writer.write(bb.getBlob('image/png'));
      
          }, onError);
        }, onError);
      }, onError);
    };
      
    xhr.send();

    2)分割文件并上传各个部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder ||
                         window.BlobBuilder;
      
    function upload(blobOrFile) {
      var xhr = new XMLHttpRequest();
      xhr.open('POST''/server'true);
      xhr.onload = function(e) { ... };
      xhr.send(blobOrFile);
    }
      
    document.querySelector('input[type="file"]').addEventListener('change',function(e) {
      var blob = this.files[0];
      
      const BYTES_PER_CHUNK = 1024 * 1024; // 1MB chunk sizes.
      const SIZE = blob.size;
      
      var start = 0;
      var end = BYTES_PER_CHUNK;
      
      while(start < SIZE) {
      
        // Note: blob.slice has changed semantics and been prefixed. Seehttp://goo.gl/U9mE5.
        if ('mozSlice' in blob) {
          var chunk = blob.mozSlice(start, end);
        else {
          var chunk = blob.webkitSlice(start, end);
        }
      
        upload(chunk);
      
        start = end;
        end = start + BYTES_PER_CHUNK;
      }
    }, false);
      
    })();

     

    http://mozilla.com.cn/post/34886/

  • 相关阅读:
    sort color (荷兰国旗)
    先序遍历和后序遍历构建二叉树
    二叉树的遍历
    排序
    内存相关内容
    chrome控制台console方法表
    记一次移动端CSS引发的小Bug
    JavaScript的事件
    浅谈webpack打包原理
    JS模块化进程
  • 原文地址:https://www.cnblogs.com/daishuguang/p/3972001.html
Copyright © 2020-2023  润新知