请选择 进入手机版 | 继续访问电脑版
Mozilla

火狐社区

登录    注册

用新浪微博连接 QQ互联

[综合讨论] XMLHttpRequest2的进步之处

_少龙 游客 发表于 2012-7-31 11:46:45 | 显示全部楼层 |阅读模式 [复制链接]
0 4582

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

HTML5是现在web开发中的热点,虽然关于web app和local app一直有争论,但是从技术学习的角度,html5技术无疑是值得学习的。最近看了看XHR2,大概了解了其中比之前进步的要点,记录下来以备日后复习:
首先,XHR2的官方注解可见:http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html
XHR2主要的新功能有(我平时开发遇到的):
  • 上传下载二进制数据

  • 上传进度事件的支持

  • 跨域请求



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


一) 二进制数据处理
以前通过 XHR 抓取二进制 blob 形式的文件是很痛苦的事情。从技术上来说,这甚至是不可能的实现。有一种广为流传的一种技巧,是将 MIME 类型替换为由用户定义的字符集,如下所示:
  1. var xhr = new XMLHttpRequest();
  2. xhr.open('GET', '/path/to/image.png', true);
  3.    
  4. xhr.overrideMimeType('text/plain; charset=x-user-defined');
  5.    
  6. xhr.onreadystatechange = function(e) {
  7.   if (this.readyState == 4 && this.status == 200) {
  8.     var binStr = this.responseText;
  9.     for (var i = 0, len = binStr.length; i < len; ++i) {
  10.       var c = binStr.charCodeAt(i);
  11.       //String.fromCharCode(c & 0xff);
  12.       var byte = c & 0xff;
  13.     }
  14.   }
  15. };
  16.    
  17. xhr.send();
复制代码
虽然这种方法可行,但是 responseText 中实际返回的并不是二进制 blob,而是代表图片文件的二进制字符串。我们要巧妙地让服务器在不作处理的情况下,将这些数据传递回去。
现在XHR2中,新增了responseType和response属性,可以告知浏览器我们希望返回什么格式的数据。
  • xhr.responseType
    在发送请求前,根据您的数据需要,将 xhr.responseType 设置为“text”、“arraybuffer”、“blob”或“document”。请注意,设置(或忽略)xhr.responseType = '' 会默认将响应设为“text”。

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


凭借这个优秀的新属性,我们可以修改上一个示例:以 ArrayBuffer 而非字符串的形式抓取图片。将缓冲区移交给 BlobBuilder API 可创建 Blob:
  1. BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
  2.    
  3. var xhr = new XMLHttpRequest();
  4. xhr.open('GET', '/path/to/image.png', true);
  5. xhr.responseType = 'arraybuffer';
  6.    
  7. xhr.onload = function(e) {
  8.   if (this.status == 200) {
  9.     var bb = new BlobBuilder();
  10.     bb.append(this.response); // Note: not xhr.responseText
  11.    
  12.     var blob = bb.getBlob('image/png');
  13.     ...
  14.   }
  15. };
  16.    
  17. xhr.send();
复制代码
此外ArrayBuffer是二进制数据通用的固定长度容器。如果您需要原始数据的通用缓冲区,ArrayBuffer 就非常好用,但是它真正强大的功能是让您使用 JavaScript 类型数组创建底层数据的“视图”。实际上,可以通过单个 ArrayBuffer 来源创建多个视图。例如,您可以创建一个 8 位整数数组,与来自相同数据的现有 32 位整数数组共享同一个 ArrayBuffer。底层数据保持不变,我们只是创建其不同的表示方法。
  1. var xhr = new XMLHttpRequest();
  2. xhr.open('GET', '/path/to/image.png', true);
  3. xhr.responseType = 'arraybuffer';
  4.    
  5. xhr.onload = function(e) {
  6.   var uInt8Array = new Uint8Array(this.response); // this.response == uInt8Array.buffer
  7.   // var byte3 = uInt8Array[4]; // byte at offset 4
  8.   ...
  9. };
  10.    
  11. xhr.send();
复制代码
如果您要直接处理 Blob 且/或不需要操作任何文件的字节,可使用xhr.responseType='blob'
  1. window.URL = window.URL || window.webkitURL;  // Take care of vendor prefixes.
  2.    
  3. var xhr = new XMLHttpRequest();
  4. xhr.open('GET', '/path/to/image.png', true);
  5. xhr.responseType = 'blob';
  6.    
  7. xhr.onload = function(e) {
  8.   if (this.status == 200) {
  9.     var blob = this.response;
  10.    
  11.     var img = document.createElement('img');
  12.     img.onload = function(e) {
  13.       window.URL.revokeObjectURL(img.src); // Clean up after yourself.
  14.     };
  15.     img.src = window.URL.createObjectURL(blob);
  16.     document.body.appendChild(img);
  17.     ...
  18.   }
  19. };
  20.    
  21. xhr.send();
复制代码
Blob 可用于很多场合,包括保存到 indexedDB、写入 HTML5 文件系统 或创建 Blob 网址(如本例中所示)。
二)发送数据
能够下载各种格式的数据固然是件好事,但是如果不能将这些丰富格式的数据送回本垒(服务器),那就毫无意义了。XMLHttpRequest 有时候会限制我们发送 DOMString 或 Document (XML) 数据。但是现在不会了。现已替换成经过修改的 send() 方法,可接受以下任何类型:DOMString、Document、FormData、Blob、File、ArrayBuffer。本部分的其余内容中的示例演示了如何使用各类型发送数据。
1)发送字符串数据:xhr.send(DOMString)
  1. function sendText(txt) {
  2.   var xhr = new XMLHttpRequest();
  3.   xhr.open('POST', '/server', true);
  4.   xhr.responseType ='text';  xhr.onload = function(e) {
  5.     if (this.status == 200) {
  6.       console.log(this.responseText);
  7.     }
  8.   };
  9.    
  10.   xhr.send(txt);
  11. }
  12.    
  13. sendText('test string');
复制代码
2)提交表单:xhr.send(FormData)
  1. function sendForm() {
  2.   var formData = new FormData();
  3.   formData.append('username', 'johndoe');
  4.   formData.append('id', 123456);
  5.    
  6.   var xhr = new XMLHttpRequest();
  7.   xhr.open('POST', '/server', true);
  8.   xhr.onload = function(e) { ... };
  9.    
  10.   xhr.send(formData);
  11. }
复制代码
由form数据初始化formData
  1. <form id="myform" name="myform" action="/server">
  2.   <input type="text" name="username" value="johndoe">
  3.   <input type="number" name="id" value="123456">
  4.   <input type="submit" onclick="return sendForm(this.form);">
  5. </form>
复制代码
  1. function sendForm(form) {
  2.   var formData = new FormData(form);
  3.    
  4.   formData.append('secret_token', '1234567890'); // Append extra data before send.
  5.    
  6.   var xhr = new XMLHttpRequest();
  7.   xhr.open('POST', form.action, true);
  8.   xhr.onload = function(e) { ... };
  9.    
  10.   xhr.send(formData);
  11.    
  12.   return false; // Prevent page from submitting.
  13. }
复制代码
同时可以包含文件上传
  1. function uploadFiles(url, files) {
  2.   var formData = new FormData();
  3.    
  4.   for (var i = 0, file; file = files[i]; ++i) {
  5.     formData.append(file.name, file);
  6.   }
  7.    
  8.   var xhr = new XMLHttpRequest();
  9.   xhr.open('POST', url, true);
  10.   xhr.onload = function(e) { ... };
  11.    
  12.   xhr.send(formData);  // multipart/form-data
  13. }
  14.    
  15. document.querySelector('input[type="file"]').addEventListener('change', function(e) {
  16.   uploadFiles('/server', this.files);
  17. }, false);
复制代码
3)上传文件或 blob:xhr.send(Blob),同时demo下上传事件如果使用
  1. <progress min="0" max="100" value="0">0% complete</progress>
复制代码
  1. function upload(blobOrFile) {
  2.   var xhr = new XMLHttpRequest();
  3.   xhr.open('POST', '/server', true);
  4.   xhr.onload = function(e) { ... };
  5.    
  6.   // Listen to the upload progress.
  7.   var progressBar = document.querySelector('progress');
  8.   xhr.upload.onprogress = function(e) {
  9.     if (e.lengthComputable) {
  10.       progressBar.value = (e.loaded / e.total) * 100;
  11.       progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
  12.     }
  13.   };
  14.    
  15.   xhr.send(blobOrFile);
  16. }
  17.    
  18. // Take care of vendor prefixes.
  19. BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
  20.    
  21. var bb = new BlobBuilder();
  22. bb.append('hello world');
  23.    
  24. upload(bb.getBlob('text/plain'));
复制代码
4)上传字节:xhr.send(ArrayBuffer)
  1. function sendArrayBuffer() {
  2.   var xhr = new XMLHttpRequest();
  3.   xhr.open('POST', '/server', true);
  4.   xhr.onload = function(e) { ... };
  5.    
  6.   var uInt8Array = new Uint8Array([1, 2, 3]);
  7.    
  8.   xhr.send(uInt8Array.buffer);
  9. }
复制代码
三)跨源请求 (CORS)

CORS 允许一个域上的网络应用向另一个域提交跨域 AJAX 请求。启用此功能非常简单,只需由服务器发送一个响应标头即可。
允许来自 example.com 的请求:
  1. Access-Control-Allow-Origin: http://example.com
复制代码
要允许任何域向您提交请求:
  1. Access-Control-Allow-Origin: *
复制代码
提交跨域请求时和以前没有区别。
  1. var xhr = new XMLHttpRequest();
  2. xhr.open('GET', 'http://www.example2.com/hello.json');
  3. xhr.onload = function(e) {
  4.   var data = JSON.parse(this.response);
  5.   ...
  6. }
  7. xhr.send();
复制代码
四)有用的实例
1)下载文件并保存到文件系统
  1. window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;
  2.    
  3. function onError(e) {
  4.   console.log('Error', e);
  5. }
  6.    
  7. var xhr = new XMLHttpRequest();
  8. xhr.open('GET', '/path/to/image.png', true);
  9. xhr.responseType = 'arraybuffer';
  10.    
  11. xhr.onload = function(e) {
  12.    
  13.   window.requestFileSystem(TEMPORARY, 1024 * 1024, function(fs) {
  14.     fs.root.getFile('image.png', {create: true}, function(fileEntry) {
  15.       fileEntry.createWriter(function(writer) {
  16.    
  17.         writer.onwrite = function(e) { ... };
  18.         writer.onerror = function(e) { ... };
  19.    
  20.         var bb = new BlobBuilder();
  21.         bb.append(xhr.response);
  22.    
  23.         writer.write(bb.getBlob('image/png'));
  24.    
  25.       }, onError);
  26.     }, onError);
  27.   }, onError);
  28. };
  29.    
  30. xhr.send();
复制代码
2)分割文件并上传各个部分
  1. window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder ||
  2.                      window.BlobBuilder;
  3.    
  4. function upload(blobOrFile) {
  5.   var xhr = new XMLHttpRequest();
  6.   xhr.open('POST', '/server', true);
  7.   xhr.onload = function(e) { ... };
  8.   xhr.send(blobOrFile);
  9. }
  10.    
  11. document.querySelector('input[type="file"]').addEventListener('change', function(e) {
  12.   var blob = this.files[0];
  13.    
  14.   const BYTES_PER_CHUNK = 1024 * 1024; // 1MB chunk sizes.
  15.   const SIZE = blob.size;
  16.    
  17.   var start = 0;
  18.   var end = BYTES_PER_CHUNK;
  19.    
  20.   while(start < SIZE) {
  21.    
  22.     // Note: blob.slice has changed semantics and been prefixed. See http://goo.gl/U9mE5.
  23.     if ('mozSlice' in blob) {
  24.       var chunk = blob.mozSlice(start, end);
  25.     } else {
  26.       var chunk = blob.webkitSlice(start, end);
  27.     }
  28.    
  29.     upload(chunk);
  30.    
  31.     start = end;
  32.     end = start + BYTES_PER_CHUNK;
  33.   }
  34. }, false);
  35.    
  36. })();
复制代码


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发新帖
论坛更多 »
火狐微信
快速回复 返回顶部 返回列表