」XMLHttpRequest 的異步調用網上找的例子運行沒問題,但稍微改了一點點就報錯」InvalidStateError: XMLHttpRequest has an invalid context「。斷斷續續 搞了3天終於通了,能夠接收二進制文件了。 「 以後找到了下篇文章,發現我所使用的方法是下方中的老方法。準備再按下文所說新方法試試。php
下文是2011寫的了,想必所說內容到如今已經是全部瀏覽器都支持的了吧。html
如下轉自: https://www.html5rocks.com/zh/tutorials/file/xhr2/。html5
HTML5 Rocksjquery
By Eric Bidelmanweb
已發佈: 五月 27th, 2011 Comments: 0apache
HTML5 世界中有這樣一位無名英雄:XMLHttpRequest
。嚴格地說,XHR2 並不屬於 HTML5。不過,它是瀏覽器供應商對於核心平臺不斷作出的改進中的一部分。我之因此將 XHR2 加入咱們新的百寶囊中,就是由於它在現在複雜的網絡應用中扮演了不可或缺的角色。json
結果呢,咱們這位老朋友來了個大變身,不少人都不知道它的新功能了。2 級 XMLHttpRequest 引入了大量的新功能(例如跨源請求、上傳進度事件以及對上傳/下載二進制數據的支持等),一舉封殺了咱們網絡應用中的瘋狂黑客。這使得 AJAX 能夠與不少尖端的 HTML5 API 結合使用,例如 File System API、Web Audio API 和 WebGL。api
此教程重點介紹 XMLHttpRequest
中的新功能,尤爲是可用於處理文件的功能。跨域
之前經過 XHR 抓取二進制 blob 形式的文件是很痛苦的事情。從技術上來講,這甚至是不可能的實現。有一種廣爲流傳的一種技巧,是將 MIME 類型替換爲由用戶定義的字符集,以下所示:數組
提取圖片的舊方法:
var xhr = new XMLHttpRequest(); xhr.open('GET', '/path/to/image.png', true); // Hack to pass bytes through unprocessed. 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; // byte at offset i } } }; xhr.send();
雖然這種方法可行,可是 responseText
中實際返回的並非二進制 blob,而是表明圖片文件的二進制字符串。咱們要巧妙地讓服務器在不做處理的狀況下,將這些數據傳遞回去。雖然這個技巧有用,可是我不推薦你們走這種歪門邪道。只要是經過玩弄字符代碼和字符串操控技巧,強行將數據轉化成所需的格式,都會出現問題。
在前一個示例中,咱們經過替換服務器的 MIME 類型並將響應文本做爲二進制字符串處理,下載了二進制「文件」形式的圖片。如今,讓咱們利用 XMLHttpRequest
新增的 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
:
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
。底層數據保持不變,咱們只是建立其不一樣的表示方法。
例如,下面以 ArrayBuffer
的形式抓取咱們相同的圖片,可是如今,會經過該數據緩衝區建立無符號的 8 位整數數組。
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'
:
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
。本部分的其他內容中的示例演示瞭如何使用各種型發送數據。
function sendText(txt) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/server', true); xhr.onload = function(e) { if (this.status == 200) { console.log(this.responseText); } }; xhr.send(txt); } sendText('test string');
function sendTextNew(txt) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/server', true); xhr.responseType = 'text'; xhr.onload = function(e) { if (this.status == 200) { console.log(this.response); } }; xhr.send(txt); } sendText2('test string');
這沒有新內容,只是正確的代碼段略有不一樣。其中設置了 responseType='text'
做爲對比。再次說明,省略此行會獲得一樣的結果。
不少人可能習慣於使用 jQuery 插件或其餘庫來處理 AJAX 表單提交。而咱們能夠改用 FormData
,這是另外一種針對 XHR2 設計的新數據類型。使用 FormData
可以很方便地實時以 JavaScript 建立 HTML <form>
。而後可使用 AJAX 提交該表單:
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>
,並經過調用 append 方法爲其附加了 <input>
值。
固然,您無需從一開始就建立 <form>
。FormData
對象可經過頁面上現有的 HTMLFormElement
進行初始化。例如:
<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>
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. }
HTML 表單可包含文件上傳(例如 <input type="file">
),而 FormData
也能夠處理此操做。只需附加文件,瀏覽器就會在調用 send()
時構建 multipart/form-data
請求。
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);
咱們也可使用 XHR 發送 File
或 Blob
。請注意,全部 File
都是 Blob
,因此在此使用二者皆可。
該示例使用 BlobBuilder
API 從頭開始建立新的文本文件,並將該 Blob
上傳到服務器。該代碼還設置了一個處理程序,用於通知用戶上傳進度:
<progress min="0" max="100" value="0">0% complete</progress>
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'));
最後也是至關重要的一點就是,咱們能以 XHR 的有效負載形式發送 ArrayBuffer
。
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 容許一個域上的網絡應用向另外一個域提交跨域 AJAX 請求。啓用此功能很是簡單,只需由服務器發送一個響應標頭便可。
假設您的應用已經在 example.com
上了,而您想要從 www.example2.com
提取數據。通常狀況下,若是您嘗試進行這種類型的 AJAX 調用,請求將會失敗,而瀏覽器將會出現「源不匹配」的錯誤。利用 CORS,www.example2.com
只需添加一個標頭,就能夠容許來自 example.com
的請求:
Access-Control-Allow-Origin: http://example.com
可將 Access-Control-Allow-Origin
添加到某網站下或整個域中的單個資源。要容許任何域向您提交請求,請設置以下:
Access-Control-Allow-Origin: *
其實,該網站 (html5rocks.com) 已在其全部網頁上均啓用了 CORS。啓用開發人員工具後,您就會在咱們的響應中看到 Access-Control-Allow-Origin
了:
html5rocks.com 上的 Access-Control-Allow-Origin
標頭
啓用跨源請求是很是簡單的,所以若是您的數據是公開的,請務必啓用 CORS!
若是服務器端已啓用了 CORS,那麼提交跨域請求就和普通的 XMLHttpRequest
請求沒什麼區別。例如,如今 example.com
能夠向 www.example2.com
提交請求了:
var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://www.example2.com/hello.json'); xhr.onload = function(e) { var data = JSON.parse(this.response); ... } xhr.send();
假設您有一個圖片庫,想要提取一些圖片,而後使用 HTML5 文件系統本地保存這些圖片。一種方法是以 ArrayBuffer
形式請求圖片,經過數據構建 Blob
,並使用 FileWriter
寫入 blob:
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();
請注意:要使用此代碼,請參閱「探索 FileSystem API」教程中的瀏覽器支持和存儲限制。
使用 File API,咱們能夠將操做簡化爲上傳大文件。咱們採用的技術是:將要上傳的文件分割成多個部分,爲每一個部分生成一個 XHR,而後在服務器上將各部分組合成文件。這相似於 Gmail 快速上傳大附件的方法。使用這種技術還能夠規避 Google 應用引擎對 http 請求的 32 MB 限制。
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. See http://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); })();
用於在服務器上重組文件的代碼並未在此顯示。
趕快試試吧!
#bytes/chunk:
Enjoyed this article? Grab the RSS feed and stay up-to-date.
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the Apache 2.0 License.