XMLHttpRequest是一個瀏覽器接口,使得Javascript能夠進行HTTP(S)通訊。php
最先,微軟在IE 5引進了這個接口。由於它太有用,其餘瀏覽器也模仿部署了,ajax操做所以得以誕生。css
可是,這個接口一直沒有標準化,每家瀏覽器的實現或多或少有點不一樣。HTML 5的概念造成後,W3C開始考慮標準化這個接口。2008年2月,就提出了XMLHttpRequest Level 2 草案。html
這個XMLHttpRequest的新版本,提出了不少有用的新功能,將大大推進互聯網革新。本文就對這個新版本進行詳細介紹。html5
1、老版本的XMLHttpRequest對象java
在介紹新版本以前,咱們先回顧一下老版本的用法。node
首先,新建一個XMLHttpRequest的實例。es6
var xhr = new XMLHttpRequest();web
而後,向遠程主機發出一個HTTP請求。ajax
xhr.open('GET', 'example.php');
xhr.send();
接着,就等待遠程主機作出迴應。這時須要監控XMLHttpRequest對象的狀態變化,指定回調函數。
xhr.onreadystatechange = function(){
if ( xhr.readyState == 4 && xhr.status == 200 ) {
alert( xhr.responseText );
} else {
alert( xhr.statusText );
}
};
上面的代碼包含了老版本XMLHttpRequest對象的主要屬性:
* xhr.readyState:XMLHttpRequest對象的狀態,等於4表示數據已經接收完畢。
* xhr.status:服務器返回的狀態碼,等於200表示一切正常。
* xhr.responseText:服務器返回的文本數據
* xhr.responseXML:服務器返回的XML格式的數據
* xhr.statusText:服務器返回的狀態文本。
2、老版本的缺點
老版本的XMLHttpRequest對象有如下幾個缺點:
* 只支持文本數據的傳送,沒法用來讀取和上傳二進制文件。
* 傳送和接收數據時,沒有進度信息,只能提示有沒有完成。
* 受到"同域限制"(Same Origin Policy),只能向同一域名的服務器請求數據。
3、新版本的功能
新版本的XMLHttpRequest對象,針對老版本的缺點,作出了大幅改進。
* 能夠設置HTTP請求的時限。
* 可使用FormData對象管理表單數據。
* 能夠上傳文件。
* 能夠請求不一樣域名下的數據(跨域請求)。
* 能夠獲取服務器端的二進制數據。
* 能夠得到數據傳輸的進度信息。
下面,我就一一介紹這些新功能。
4、HTTP請求的時限
有時,ajax操做很耗時,並且沒法預知要花多少時間。若是網速很慢,用戶可能要等好久。
新版本的XMLHttpRequest對象,增長了timeout屬性,能夠設置HTTP請求的時限。
xhr.timeout = 3000;
上面的語句,將最長等待時間設爲3000毫秒。過了這個時限,就自動中止HTTP請求。與之配套的還有一個timeout事件,用來指定回調函數。
xhr.ontimeout = function(event){
alert('請求超時!');
}
目前,Opera、Firefox和IE 10支持該屬性,IE 8和IE 9的這個屬性屬於XDomainRequest對象,而Chrome和Safari還不支持。
5、FormData對象
ajax操做每每用來傳遞表單數據。爲了方便表單處理,HTML 5新增了一個FormData對象,能夠模擬表單。
首先,新建一個FormData對象。
var formData = new FormData();
而後,爲它添加表單項。
formData.append('username', '張三');
formData.append('id', 123456);
最後,直接傳送這個FormData對象。這與提交網頁表單的效果,徹底同樣。
xhr.send(formData);
FormData對象也能夠用來獲取網頁表單的值。
var form = document.getElementById('myform');
var formData = new FormData(form);
formData.append('secret', '123456'); // 添加一個表單項
xhr.open('POST', form.action);
xhr.send(formData);
6、上傳文件
新版XMLHttpRequest對象,不只能夠發送文本信息,還能夠上傳文件。
假定files是一個"選擇文件"的表單元素(input[type="file"]),咱們將它裝入FormData對象。
var formData = new FormData();
for (var i = 0; i < files.length;i++) {
formData.append('files[]', files[i]);
}
而後,發送這個FormData對象。
xhr.send(formData);
7、跨域資源共享(CORS)
新版本的XMLHttpRequest對象,能夠向不一樣域名的服務器發出HTTP請求。這叫作"跨域資源共享"(Cross-origin resource sharing,簡稱CORS)。
使用"跨域資源共享"的前提,是瀏覽器必須支持這個功能,並且服務器端必須贊成這種"跨域"。若是可以知足上面的條件,則代碼的寫法與不跨域的請求徹底同樣。
xhr.open('GET', 'http://other.server/and/path/to/script');
目前,除了IE 8和IE 9,主流瀏覽器都支持CORS,IE 10也將支持這個功能。服務器端的設置,請參考《Server-Side Access Control》。
8、接收二進制數據(方法A:改寫MIMEType)
老版本的XMLHttpRequest對象,只能從服務器取回文本數據(不然它的名字就不用XML起首了),新版則能夠取回二進制數據。
這裏又分紅兩種作法。較老的作法是改寫數據的MIMEType,將服務器返回的二進制數據假裝成文本數據,而且告訴瀏覽器這是用戶自定義的字符集。
xhr.overrideMimeType("text/plain; charset=x-user-defined");
而後,用responseText屬性接收服務器返回的二進制數據。
var binStr = xhr.responseText;
因爲這時,瀏覽器把它當作文本數據,因此還必須再一個個字節地還原成二進制數據。
for (var i = 0, len = binStr.length; i < len; ++i) {
var c = binStr.charCodeAt(i);
var byte = c & 0xff;
}
最後一行的位運算"c & 0xff",表示在每一個字符的兩個字節之中,只保留後一個字節,將前一個字節扔掉。緣由是瀏覽器解讀字符的時候,會把字符自動解讀成Unicode的0xF700-0xF7ff區段。
8、接收二進制數據(方法B:responseType屬性)
從服務器取回二進制數據,較新的方法是使用新增的responseType屬性。若是服務器返回文本數據,這個屬性的值是"TEXT",這是默認值。較新的瀏覽器還支持其餘值,也就是說,能夠接收其餘格式的數據。
你能夠把responseType設爲blob,表示服務器傳回的是二進制對象。
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = 'blob';
接收數據的時候,用瀏覽器自帶的Blob對象便可。
var blob = new Blob([xhr.response], {type: 'image/png'});
注意,是讀取xhr.response,而不是xhr.responseText。
你還能夠將responseType設爲arraybuffer,把二進制數據裝在一個數組裏。
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = "arraybuffer";
接收數據的時候,須要遍歷這個數組。
var arrayBuffer = xhr.response;
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteArray.byteLength; i++) {
// do something
}
}
更詳細的討論,請看Sending and Receiving Binary Data。
9、進度信息
新版本的XMLHttpRequest對象,傳送數據的時候,有一個progress事件,用來返回進度信息。
它分紅上傳和下載兩種狀況。下載的progress事件屬於XMLHttpRequest對象,上傳的progress事件屬於XMLHttpRequest.upload對象。
咱們先定義progress事件的回調函數。
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
而後,在回調函數裏面,使用這個事件的一些屬性。
function updateProgress(event) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
}
}
上面的代碼中,event.total是須要傳輸的總字節,event.loaded是已經傳輸的字節。若是event.lengthComputable不爲真,則event.total等於0。
與progress事件相關的,還有其餘五個事件,能夠分別指定回調函數:
* load事件:傳輸成功完成。
* abort事件:傳輸被用戶取消。
* error事件:傳輸中出現錯誤。
* loadstart事件:傳輸開始。
* loadEnd事件:傳輸結束,可是不知道成功仍是失敗。
10、閱讀材料
1. Introduction to XMLHttpRequest Level 2: 新功能的綜合介紹。
2. New Tricks in XMLHttpRequest 2:一些用法的介紹。
3. Using XMLHttpRequest:一些高級用法,主要針對Firefox瀏覽器。
4. HTTP Access Control:CORS綜述。
5. DOM access control using cross-origin resource sharing:CORS的9種HTTP頭信息
6. Server-Side Access Control:服務器端CORS設置。
7. Enable CORS:服務端CORS設置。
(完)
qiayue 說:
學習了,html5的新特性真讓人着迷
2012年9月 9日 10:21 | # | 引用
RedNax 說:
其實IE8,9也支持跨域資源共享,不過用的是 XDomainRequest 對象,並且作法和w3c標準不一樣,不是設置在header裏而是要放一個文件在服務器上(相似Flash跨域的作法)。
2012年9月 9日 23:21 | # | 引用
ToFishes 說:
IE8,9也支持跨域沒有錯,可用接口單一,可是並不須要放一個文件在服務器上,也不相似flash。
我作過測試: http://cssor.com/cross-origin-resource-sharing.html
2012年9月10日 10:59 | # | 引用
馬猴 說:
剛看了《黑客與畫家》,確實是一本不可多得的好書!
2012年9月10日 20:41 | # | 引用
RedNax 說:
2012年9月10日 20:51 | # | 引用
dirty 說:
Websocket比xhr更強大,能夠實現實時的push,如今只能靠flash,隨着支持瀏覽器的普及,將會看到更多用websocket的網站,阮先生也能夠介紹一下
2012年9月11日 11:24 | # | 引用
土木罈子 說:
最近的內容涉及到好多技術的細節問題,沒有太大的興趣,固然了,寫什麼是阮兄的自由。
2012年9月12日 05:20 | # | 引用
瞬間的永恆 說:
js跨域還在研究當中,學習啦
2012年9月24日 16:37 | # | 引用
Jak Wings 說:
不錯,假如能夠順便知道支持的瀏覽器的大體版本號就行了。
2012年10月 2日 15:54 | # | 引用
甄碼農 說:
據我瞭解文中提到的「跨域資源共享(CORS)」目前仍是w3c的草案,尚未正式發佈。
2012年11月22日 17:22 | # | 引用
keshin 說:
博主,關於CORS,最好再加上Preflighted request的內容
若是要使用除GET和POST之外的方法,或者想用content-type不是application/x-www-form-urlencoded, multipart/form-data, or text/plain 的POST,須要在request的header中添加額外的custom header
Preflighted requests
Unlike simple requests (discussed above), "preflighted" requests first send an HTTP OPTIONS request header to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:
It uses methods other than GET or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#Preflighted_requests
http://arunranga.com/examples/access-control/preflightInvocation.html
2013年2月 6日 14:32 | # | 引用
Jak Wings 說:
這裏有很多的Demo能夠參考:http://tiffanybbrown.com/presentation/2011/xhr2/
2013年2月20日 08:55 | # | 引用
jalorchen 說:
樓主您好,請問您本人是否試過XMLHttpRequest上傳文件?若是有,請指教,最好是您完整的JS代碼,我無惡意,經過實踐以後只是對XMLHttpRequest上傳文件表示質疑。畢竟他歸根結底仍是一個AJAX,歡迎QQ或是郵件交流和指點,謝謝O(∩_∩)O.qq:303834036
2013年3月17日 18:37 | # | 引用
pepepe 說:
正在學習跨文檔通訊 謝謝樓主的資料0w0
2013年4月16日 21:08 | # | 引用
Thyiad 說:
2014年3月17日 14:05 | # | 引用
歐文 說:
是呀,阮老師能夠介紹一下web socket.
2014年4月11日 19:48 | # | 引用
jianwu5 說:
Flash的錯在於它只是一個插件,小三永遠轉不了正。我下載新版firefox後,一啓動就提示Flash崩潰,用戶遇到這種狀況怎麼跟他解析?!
2014年10月17日 23:51 | # | 引用
189 說:
chrome 38.0已經支持xhr的timeout了
2014年11月 3日 13:53 | # | 引用
jalorchen 說:
謝謝兄弟指點,我會按照您的指點嘗試一下。
2015年3月21日 15:25 | # | 引用
請問阮老師 說:
請問發送數據時,能否發送blob數據?文章只說了接收數據能夠接收blob
2015年7月25日 17:37 | # | 引用
孔明 說:
謝謝阮兄,恰好要用到這個知識點!
2015年11月 6日 11:19 | # | 引用
山姆大叔 說:
使用阮老師提供的formData對象上傳的文件(異步方式),後臺(java)不知道該如何解析啊,求各位大神指點,謝謝!
2016年1月14日 22:28 | # | 引用
微涼的傾城時光 說:
謝謝樓主大大!感受收穫良多!
2016年3月20日 19:02 | # | 引用
momognu 說:
使用起來和Android的AsyncTask挺像啊
2016年3月28日 12:23 | # | 引用
老K 說:
2016年5月 4日 17:14 | # | 引用
老K 說:
void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);
2016年5月 4日 17:17 | # | 引用
戈雅 說:
問下,關於上傳進度 【event.loaded是已經傳輸的字節】 event裏面的數據是從本地獲取的仍是服務的返回的?
2016年6月12日 15:28 | # | 引用
阮老師你好 說:
onreadystatechange應該在open和send以前設定吧,不然一些狀態會捕捉不到
2016年7月29日 00:30 | # | 引用
real 說:
如今使用onload
2017年3月17日 12:10 | # | 引用
小小蘇 說:
文件進度須要服務端支持把,能不能寫幾行服務端的僞代碼,咱們也好理解
2018年3月28日 12:50 | # | 引用
warryy 說:
這個應該不須要服務端支持吧
2018年3月28日 15:30 | # | 引用