JS中使用FormData上傳文件、圖片的方法

參考博客:http://www.cnblogs.com/xiaohuochai/p/6543019.html

http://www.cnblogs.com/qcloud1001/p/6839541.html

使用CORS 實現Ajax的另類跨域:關於瀏覽器端,以jQuery爲例,咱們須要在ajax請求中加一個參數xhrFields,並設置爲withCredentials : true。

www.cnblogs.com/Darren_code…
javascript

/** 
 * 防止瀏覽器緩存頁面或請求結果 
 * @author XuJijun 
 * 
 */  
public class NoCacheFilter implements Filter {  
  
    @Override  
    public void destroy(){  
    }  
  
    @Override  
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {  
        HttpServletResponse response = (HttpServletResponse)resp;  
  
        response.setDateHeader("Expires", -1);  
        response.setHeader("Cache_Control", "no-cache");  
        response.setHeader("Pragma", "no-cache");  
          
        response.setHeader("Access-Control-Allow-Origin", "*");     //容許跨域請求  
          
        chain.doFilter(req, resp);  
    }  
  
    @Override  
    public void init(FilterConfig arg0) throws ServletException {  
    }  
}  
複製代碼


默認狀況下,跨源請求不提供憑據(cookie、HTTP認證及客戶端SSL證實等)。經過將withCredentials屬性設置爲true,能夠指定某個請求應該發送憑據。若是服務器接收帶憑據的請求,會用下面的HTTP頭部來響應。php

其實就是咱們剛纔設置的第二條配置。css

雖然設置了widthCredentials爲true的請求中會包含遠程域的全部cookie,但這些cookie仍然遵循同源策略,因此外域是訪問不了這些cookie的,如今咱們就能夠安全地跨域訪問啦。html




www.cnblogs.com/xiaohuochai…
前端

1、input:file屬性
屬性值有如下幾個比較經常使用:
accept:表示能夠選擇的文件MIME類型,多個MIME類型用英文逗號分開,經常使用的MIME類型見下表。
multiple:是否能夠選擇多個文件,多個文件時其value值爲第一個文件的虛擬路徑。
一、accept
只能選擇png和gif圖片
<input id="fileId1" type="file" accept="image/png,image/gif" name="file" />
二、multiple
多文件上傳
<input id="fileId2" type="file" multiple="multiple" name="file" />
三、經常使用MIME類型
後綴名       MIME名稱
*.3gpp    audio/3gpp, video/3gpp
*.ac3    audio/ac3
*.asf       allpication/vnd.ms-asf
*.au           audio/basic
*.css           text/css
*.csv           text/csv
*.doc    application/msword    
*.dot    application/msword    
*.dtd    application/xml-dtd    
*.dwg    image/vnd.dwg    
*.dxf      image/vnd.dxf
*.gif            image/gif    
*.htm    text/html    
*.html    text/html    
*.jp2            image/jp2    
*.jpe       image/jpeg
*.jpeg    image/jpeg
*.jpg          image/jpeg    
*.js       text/javascript, application/javascript    
*.json    application/json    
*.mp2    audio/mpeg, video/mpeg    
*.mp3    audio/mpeg    
*.mp4    audio/mp4, video/mp4    
*.mpeg    video/mpeg    
*.mpg    video/mpeg    
*.mpp    application/vnd.ms-project    
*.ogg    application/ogg, audio/ogg    
*.pdf    application/pdf    
*.png    image/png    
*.pot    application/vnd.ms-powerpoint    
*.pps    application/vnd.ms-powerpoint    
*.ppt    application/vnd.ms-powerpoint    
*.rtf            application/rtf, text/rtf    
*.svf           image/vnd.svf    
*.tif         image/tiff    
*.tiff       image/tiff    
*.txt           text/plain    
*.wdb    application/vnd.ms-works    
*.wps    application/vnd.ms-works    
*.xhtml    application/xhtml+xml    
*.xlc      application/vnd.ms-excel    
*.xlm    application/vnd.ms-excel    
*.xls           application/vnd.ms-excel    
*.xlt      application/vnd.ms-excel    
*.xlw      application/vnd.ms-excel    
*.xml    text/xml, application/xml    
*.zip            aplication/zip    
*.xlsx     application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
2、樣式美化
請看博客:css input[type=file] 樣式美化,input上傳按鈕美化 http://www.haorooms.com/post/css_input_uploadmh
3、AJAX上傳文件
在說到ajax上傳文件,以前的文章也有說過(詳見:JS學習32:html5拖拽圖片批量ajax無刷新進度上傳)。ajax上傳的時候,須要得到input:file選擇的文件(可能爲多個文件),獲取其文件列表爲:
// input標籤的files屬性
document.querySelector("#fileId").files
// 返回的是一個文件列表數組
得到的文件列表,而後遍歷插入到表單數據當中。即:
// 得到上傳文件DOM對象
var oFiles = document.querySelector("#fileId");


// 實例化一個表單數據對象
var formData = new FormData();



// 遍歷圖片文件列表,插入到表單數據中
for (var i = 0, file; file = oFiles[i]; i++) {
    // 文件名稱,文件對象
    formData.append(file.name, file);
}
得到表單數據以後,就能夠用ajax的POST上傳。
// 實例化一個AJAX對象
var xhr = new XMLHttpRequest();
xhr.onload = function() {
    alert("上傳成功!");
}
xhr.open("POST", "upload.php", true);

// 發送表單數據
xhr.send(formData);
上傳到服務器以後,獲取到文件列表爲:
Array
(
    [jpg_jpg] => Array
        (
            [name] => jpg.jpg
            [type] => image/jpeg
            [tmp_name] => D:\xampp\tmp\phpA595.tmp
            [error] => 0
            [size] => 133363
        )

    [png_png] => Array
        (
            [name] => png.png
            [type] => image/png
            [tmp_name] => D:\xampp\tmp\phpA5A6.tmp
            [error] => 0
            [size] => 1214628
        )

)
在服務端循環遍歷這個數組就能夠上傳文件了。
複製代碼
<input id="postfile" type="file" accept="text/plain" multiple="multiple" />
複製代碼
function postFile() { //判斷是否有選擇上傳文件
			var imgPath = $("#postfile").val();
			if(imgPath == "") {
				$(".poststate").text("請選擇上傳的文本文件,以.txt後綴結尾!").css("color", "blue");
				return;
			}

			var strExtension = imgPath.substr(imgPath.lastIndexOf('.') + 1); //判斷上傳文件的後綴名
			if(strExtension != 'txt') {
				$(".poststate").text("請選擇上傳的文本文件,以.txt後綴結尾!").css("color", "blue");
				return;
			}
			var formData = new FormData();
			formData.append("file", $("#postfile")[0].files[0]);
			console.log(postfile.files[0]);
			$.ajax({
				contentType: "multipart/form-data",
				url: "s3/operationmsg/upload",
				type: "POST",
				data: formData,
				dataType: "text",
				processData: false,
				contentType: false,
				cache: false,
				beforeSend: function() {
					$(".poststate").text("正在努力上傳中,請稍後!").css("color", "green");
				},
				success: function(data) {
					var reObj = JSON.parse(data);
					if(reObj.content) {
						$(".poststate").text('"' + reObj.content.fileName + '"' + "上傳成功!").css("color", "green");
					}
				},
				error: function(XMLHttpRequest, textStatus, errorThrown) {
					$(".poststate").text("上傳失敗,請檢查網絡後重試!").css("color", "red");;
				}
			});
		}

		$("#postfile").on('change', function() { //文件上傳
			$(".poststate").text("請選擇上傳以.txt後綴結尾的文本文件!").css("color", "blue");
			postFile();
		});
複製代碼



var fileInfo =$('#inputfile').prop('files')[0];

var fd = new FormData();
                        fd.append("xxxx", fileInfo);
                        $.ajax({
                            url: "xxxx.htm",
                            type: "POST",
                            processData: false, 
                            contentType: false, 
                            dataType: 'json', 
                            data: fd, 
                            success:function(data){

                            },
                            error:function(data){

                            }
                        });
複製代碼



Ajax 知識體系大梳理

這是一篇萬字長文, 系統梳理了ajax相關的知識體系, 幾乎囊括了全部ajax的知識點.vue

原文: louiszhai.github.io/2016/11/02/…html5

導讀java

Ajax 全稱 Asynchronous JavaScript and XML, 即異步JS與XML. 它最先在IE5中被使用, 而後由Mozilla, Apple, Google推廣開來. 典型的表明應用有 Outlook Web Access, 以及 GMail. 現代網頁中幾乎無ajax不歡. 先後端分離也正是創建在ajax異步通訊的基礎之上.node

瀏覽器爲ajax作了什麼jquery

現代瀏覽器中, 雖然幾乎所有支持ajax, 但它們的技術方案卻分爲兩種:

① 標準瀏覽器經過 XMLHttpRequest 對象實現了ajax的功能. 只須要經過一行語句即可建立一個用於發送ajax請求的對象.

var xhr = new XMLHttpRequest();複製代碼

② IE瀏覽器經過 XMLHttpRequest 或者 ActiveXObject 對象一樣實現了ajax的功能.

MSXML

鑑於IE系列各類 "神級" 表現, 咱們先來看看IE瀏覽器風騷的走位.

IE下的使用環境略顯複雜, IE7及更高版本瀏覽器能夠直接使用BOM的 XMLHttpRequest 對象. MSDN傳送門: Native XMLHTTPRequest object. IE6及更低版本瀏覽器只能使用 ActiveXObject對象來建立 XMLHttpRequest 對象實例. 建立時須要指明一個相似"Microsoft.XMLHTTP"這樣的ProgID. 而實際呢, windows系統環境下, 如下ProgID都應該能夠建立XMLHTTP對象:

Microsoft.XMLHTTP
Microsoft.XMLHTTP.1.0
Msxml2.ServerXMLHTTP
Msxml2.ServerXMLHTTP.3.0
Msxml2.ServerXMLHTTP.4.0
Msxml2.ServerXMLHTTP.5.0
Msxml2.ServerXMLHTTP.6.0
Msxml2.XMLHTTP
Msxml2.XMLHTTP.3.0
Msxml2.XMLHTTP.4.0
Msxml2.XMLHTTP.5.0
Msxml2.XMLHTTP.6.0複製代碼

簡言之, Microsoft.XMLHTTP 已經很是老了, 主要用於提供對歷史遺留版本的支持, 不建議使用.對於 MSXML4, 它已被 MSXML6 替代; 而 MSXML5 又是專門針對office辦公場景, 在沒有安裝 Microsoft Office 2003 及更高版本辦公軟件的狀況下, MSXML5 未必可用. 相比之下, MSXML6 具備比 MSXML3 更穩定, 更高性能, 更安全的優點, 同時它也提供了一些 MSXML3 中沒有的功能, 好比說 XSD schema. 惟一遺憾的是, MSXML6 只在 vista 系統及以上纔是默認支持的; 而 MSXML3 在 Win2k SP4及以上系統就是可用的. 所以通常狀況下, MSXML3 能夠做爲 MSXML6 的優雅降級方案, 咱們經過指定 PorgID 爲 Msxml2.XMLHTTP 便可自動映射到 Msxml2.XMLHTTP.3.0. 以下所示:

var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3,等同於以下語句
var xhr = new ActiveXObject("MSXML2.XMLHTTP.3.0");複製代碼

MSDN有篇文章專門講解了各個版本的MSXML. 傳送門: Using the right version of MSXML in Internet Explorer.

親測了 IE5, IE5.5, IE6, IE7, IE8, IE9, IE10, IE edge等瀏覽器, IE5及以後的瀏覽器都可以經過以下語句獲取xhr對象:

var xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3
var xhr = new ActiveXObject("Microsoft.XMLHTTP");// 很老的api,雖然瀏覽器支持,功能可能不完善,故不建議使用複製代碼

以上, 思路已經很清晰了, 下面給出個全兼容的方法.

全平臺兼容的XMLHttpRequest對象

function getXHR(){
  var xhr = null;
  if(window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) { 
        alert("您的瀏覽器暫不支持Ajax!");
      }
    }
  }
  return xhr;
}複製代碼

ajax有沒有破壞js單線程機制

對於這個問題, 咱們先看下瀏覽器線程機制. 通常狀況下, 瀏覽器有以下四種線程:

  • GUI渲染線程
  • javascript引擎線程
  • 瀏覽器事件觸發線程
  • HTTP請求線程

那麼這麼多線程, 它們到底是怎麼同js引擎線程交互的呢?

一般, 它們的線程間交互以事件的方式發生, 經過事件回調的方式予以通知. 而事件回調, 又是以先進先出的方式添加到任務隊列 的末尾 , 等到js引擎空閒時, 任務隊列 中排隊的任務將會依次被執行. 這些事件回調包括 setTimeout, setInterval, click, ajax異步請求等回調.

瀏覽器中, js引擎線程會循環從 任務隊列 中讀取事件而且執行, 這種運行機制稱做 Event Loop(事件循環).

對於一個ajax請求, js引擎首先生成 XMLHttpRequest 實例對象, open事後再調用send方法. 至此, 全部的語句都是同步執行. 但從send方法內部開始, 瀏覽器爲將要發生的網絡請求建立了新的http請求線程, 這個線程獨立於js引擎線程, 因而網絡請求異步被髮送出去了. 另外一方面, js引擎並不會等待 ajax 發起的http請求收到結果, 而是直接順序往下執行.

當ajax請求被服務器響應而且收到response後, 瀏覽器事件觸發線程捕獲到了ajax的回調事件 onreadystatechange (固然也可能觸發onload, 或者 onerror等等) . 該回調事件並無被當即執行, 而是被添加到 任務隊列 的末尾. 直到js引擎空閒了, 任務隊列 的任務才被撈出來, 按照添加順序, 挨個執行, 固然也包括剛剛append到隊列末尾的 onreadystatechange 事件.

onreadystatechange 事件內部, 有可能對dom進行操做. 此時瀏覽器便會掛起js引擎線程, 轉而執行GUI渲染線程, 進行UI重繪(repaint)或者回流(reflow). 當js引擎從新執行時, GUI渲染線程又會被掛起, GUI更新將被保存起來, 等到js引擎空閒時當即被執行.

以上整個ajax請求過程當中, 有涉及到瀏覽器的4種線程. 其中除了 GUI渲染線程js引擎線程 是互斥的. 其餘線程相互之間, 都是能夠並行執行的. 經過這樣的一種方式, ajax並無破壞js的單線程機制.

ajax與setTimeout排隊問題

一般, ajax 和 setTimeout 的事件回調都被同等的對待, 按照順序自動的被添加到 任務隊列 的末尾, 等待js引擎空閒時執行. 但請注意, 並不是xhr的全部回調執行都滯後於setTImeout的回調. 請看以下代碼:

function ajax(url, method){
  var xhr = getXHR();
  xhr.onreadystatechange = function(){
      console.log('xhr.readyState:' + this.readyState);
  }
  xhr.onloadstart = function(){
      console.log('onloadStart');
  }
  xhr.onload = function(){
      console.log('onload');
  }
  xhr.open(method, url, true);
  xhr.setRequestHeader('Cache-Control',3600);
  xhr.send();
}
var timer = setTimeout(function(){
  console.log('setTimeout');
},0);
ajax('https://user-gold-cdn.xitu.io/2017/3/15/c6eacd7c2f4307f34cd45e93885d1cb6.png','GET');複製代碼

上述代碼執行結果以下圖:

ajax & setTimeout

因爲ajax異步, setTimeout回調本應該最早被執行, 然而實際上, 一次ajax請求, 並不是全部的部分都是異步的, 至少"readyState==1"的 onreadystatechange 回調以及 onloadstart 回調就是同步執行的. 所以它們的輸出排在最前面.

XMLHttpRequest 屬性解讀

首先在Chrome console下建立一個 XMLHttpRequest 實例對象xhr. 以下所示:

XMLHttpRequest

inherit

試運行如下代碼.

var xhr = new XMLHttpRequest(),
    i=0;
for(var key in xhr){
    if(xhr.hasOwnProperty(key)){
       i++;
   }
}
console.log(i);//0
console.log(XMLHttpRequest.prototype.hasOwnProperty('timeout'));//true複製代碼

可見, XMLHttpRequest 實例對象沒有自有屬性. 實際上, 它的全部屬性均來自於 XMLHttpRequest.prototype .

追根溯源, XMLHttpRequest 實例對象具備以下的繼承關係. (下面以a<<b表示a繼承b)

xhr << XMLHttpRequest.prototype << XMLHttpRequestEventTarget.prototype << EventTarget.prototype << Object.prototype

由上, xhr也具備Object等原型中的全部方法. 如toString方法.

xhr.toString();//"[object XMLHttpRequest]"複製代碼

一般, 一個xhr實例對象擁有10個普通屬性+9個方法.

readyState

只讀屬性, readyState屬性記錄了ajax調用過程當中全部可能的狀態. 它的取值簡單明瞭, 以下:

readyState對應常量描述
0 (未初始化)xhr.UNSENT請求已創建, 但未初始化(此時未調用open方法)
1 (初始化)xhr.OPENED請求已創建, 但未發送 (已調用open方法, 但未調用send方法)
2 (發送數據)xhr.HEADERS_RECEIVED請求已發送 (send方法已調用, 已收到響應頭)
3 (數據傳送中)xhr.LOADING請求處理中, 因響應內容不全, 這時經過responseBody和responseText獲取可能會出現錯誤
4 (完成)xhr.DONE數據接收完畢, 此時能夠經過經過responseBody和responseText獲取完整的響應數據

注意, readyState 是一個只讀屬性, 想要改變它的值是不可行的.

onreadystatechange

onreadystatechange事件回調方法在readystate狀態改變時觸發, 在一個收到響應的ajax請求週期中, onreadystatechange 方法會被觸發4次. 所以能夠在 onreadystatechange 方法中綁定一些事件回調, 好比:

xhr.onreadystatechange = function(e){
  if(xhr.readystate==4){
    var s = xhr.status;
    if((s >= 200 && s < 300) || s == 304){
      var resp = xhr.responseText;
      //TODO ...
    }
  }
}複製代碼

注意: onreadystatechange回調中默認會傳入Event實例, 以下:

Event

status

只讀屬性, status表示http請求的狀態, 初始值爲0. 若是服務器沒有顯式地指定狀態碼, 那麼status將被設置爲默認值, 即200.

statusText

只讀屬性, statusText表示服務器的響應狀態信息, 它是一個 UTF-16 的字符串, 請求成功且status==20X時, 返回大寫的 OK . 請求失敗時返回空字符串. 其餘狀況下返回相應的狀態描述. 好比: 301的 Moved Permanently , 302的 Found , 303的 See Other , 307 的 Temporary Redirect , 400的 Bad Request , 401的 Unauthorized 等等.

onloadstart

onloadstart事件回調方法在ajax請求發送以前觸發, 觸發時機在 readyState==1 狀態以後, readyState==2 狀態以前.

onloadstart方法中默認將傳入一個ProgressEvent事件進度對象. 以下:

ProgressEvent

ProgressEvent對象具備三個重要的Read only屬性.

  • lengthComputable 表示長度是否可計算, 它是一個布爾值, 初始值爲false.
  • loaded 表示已加載資源的大小, 若是使用http下載資源, 它僅僅表示已下載內容的大小, 而不包括http headers等. 它是一個無符號長整型, 初始值爲0.
  • total 表示資源總大小, 若是使用http下載資源, 它僅僅表示內容的總大小, 而不包括http headers等, 它一樣是一個無符號長整型, 初始值爲0.

onprogress

onprogress事件回調方法在 readyState==3 狀態時開始觸發, 默認傳入 ProgressEvent 對象, 可經過 e.loaded/e.total 來計算加載資源的進度, 該方法用於獲取資源的下載進度.

注意: 該方法適用於 IE10+ 及其餘現代瀏覽器.

xhr.onprogress = function(e){
  console.log('progress:', e.loaded/e.total);
}複製代碼

onload

onload事件回調方法在ajax請求成功後觸發, 觸發時機在 readyState==4 狀態以後.

想要捕捉到一個ajax異步請求的成功狀態, 而且執行回調, 通常下面的語句就足夠了:

xhr.onload = function(){
  var s = xhr.status;
  if((s >= 200 && s < 300) || s == 304){
    var resp = xhr.responseText;
    //TODO ...
  }
}複製代碼

onloadend

onloadend事件回調方法在ajax請求完成後觸發, 觸發時機在 readyState==4 狀態以後(收到響應時) 或者 readyState==2 狀態以後(未收到響應時).

onloadend方法中默認將傳入一個ProgressEvent事件進度對象.

timeout

timeout屬性用於指定ajax的超時時長. 經過它能夠靈活地控制ajax請求時間的上限. timeout的值知足以下規則:

  • 一般設置爲0時不生效.
  • 設置爲字符串時, 若是字符串中所有爲數字, 它會自動將字符串轉化爲數字, 反之該設置不生效.
  • 設置爲對象時, 若是該對象可以轉化爲數字, 那麼將設置爲轉化後的數字.
xhr.timeout = 0; //不生效
xhr.timeout = '123'; //生效, 值爲123
xhr.timeout = '123s'; //不生效
xhr.timeout = ['123']; //生效, 值爲123
xhr.timeout = {a:123}; //不生效複製代碼

ontimeout

ontimeout方法在ajax請求超時時觸發, 經過它能夠在ajax請求超時時作一些後續處理.

xhr.ontimeout = function(e) {
  console.error("請求超時!!!")
}複製代碼

response responseText

均爲只讀屬性, response表示服務器的響應內容, 相應的, responseText表示服務器響應內容的文本形式.

responseXML

只讀屬性, responseXML表示xml形式的響應數據, 缺省爲null, 若數據不是有效的xml, 則會報錯.

responseType

responseType表示響應的類型, 缺省爲空字符串, 可取 "arraybuffer" , "blob" , "document" , "json" , and "text" 共五種類型.

responseURL

responseURL返回ajax請求最終的URL, 若是請求中存在重定向, 那麼responseURL表示重定向以後的URL.

withCredentials

withCredentials是一個布爾值, 默認爲false, 表示跨域請求中不發送cookies等信息. 當它設置爲true時, cookies , authorization headers 或者TLS客戶端證書 均可以正常發送和接收. 顯然它的值對同域請求沒有影響.

注意: 該屬性適用於 IE10+, opera12+及其餘現代瀏覽器.

abort

abort方法用於取消ajax請求, 取消後, readyState 狀態將被設置爲 0 (UNSENT). 以下, 調用abort 方法後, 請求將被取消.

Event

getResponseHeader

getResponseHeader方法用於獲取ajax響應頭中指定name的值. 若是response headers中存在相同的name, 那麼它們的值將自動以字符串的形式鏈接在一塊兒.

console.log(xhr.getResponseHeader('Content-Type'));//"text/html"複製代碼

getAllResponseHeaders

getAllResponseHeaders方法用於獲取全部安全的ajax響應頭, 響應頭以字符串形式返回. 每一個HTTP報頭名稱和值用冒號分隔, 如key:value, 並以\r\n結束.

xhr.onreadystatechange = function() {
  if(this.readyState == this.HEADERS_RECEIVED) {
    console.log(this.getAllResponseHeaders());
  }
}
//Content-Type: text/html"複製代碼

以上, readyState === 2 狀態時, 就意味着響應頭已接受完整. 此時即可以打印出完整的 response headers.

setRequestHeader

既然能夠獲取響應頭, 那麼天然也能夠設置請求頭, setRequestHeader就是幹這個的. 以下:

//指定請求的type爲json格式
xhr.setRequestHeader("Content-type", "application/json");
//除此以外, 還能夠設置其餘的請求頭
xhr.setRequestHeader('x-requested-with', '123456');複製代碼

onerror

onerror方法用於在ajax請求出錯後執行. 一般只在網絡出現問題時或者ERR_CONNECTION_RESET時觸發(若是請求返回的是407狀態碼, chrome下也會觸發onerror).

upload

upload屬性默認返回一個 XMLHttpRequestUpload 對象, 用於上傳資源. 該對象具備以下方法:

  • onloadstart
  • onprogress
  • onabort
  • onerror
  • onload
  • ontimeout
  • onloadend

上述方法功能同 xhr 對象中同名方法一致. 其中, onprogress 事件回調方法可用於跟蹤資源上傳的進度.

xhr.upload.onprogress = function(e){
  var percent = 100 * e.loaded / e.total |0;
  console.log('upload: ' + precent + '%');
}複製代碼

overrideMimeType

overrideMimeType方法用於強制指定response 的 MIME 類型, 即強制修改response的 Content-Type . 以下, 服務器返回的response的 MIME 類型爲 text/plain .

response headers

xhr.getResponseHeader('Content-Type');//"text/plain"
xhr.responseXML;//null複製代碼

經過overrideMimeType方法將response的MIME類型設置爲 text/xml;charset=utf-8 , 以下所示:

xhr.overrideMimeType("text/xml; charset = utf-8");
xhr.send();複製代碼

此時雖然 response headers 如上圖, 沒有變化, 但 Content-Type 已替換爲新值.

xhr.getResponseHeader('Content-Type');//"text/xml; charset = utf-8"複製代碼

此時, xhr.responseXML 也將返回DOM對象, 以下圖.

response headers

XHR一級

XHR1 即 XMLHttpRequest Level 1. XHR1時, xhr對象具備以下缺點:

  • 僅支持文本數據傳輸, 沒法傳輸二進制數據.
  • 傳輸數據時, 沒有進度信息提示, 只能提示是否完成.
  • 受瀏覽器 同源策略 限制, 只能請求同域資源.
  • 沒有超時機制, 不方便掌控ajax請求節奏.

XHR二級

XHR2 即 XMLHttpRequest Level 2. XHR2針對XHR1的上述缺點作了以下改進:

  • 支持二進制數據, 能夠上傳文件, 可使用FormData對象管理表單.
  • 提供進度提示, 可經過 xhr.upload.onprogress 事件回調方法獲取傳輸進度.
  • 依然受 同源策略 限制, 這個安全機制不會變. XHR2新提供 Access-Control-Allow-Origin 等headers, 設置爲 * 時表示容許任何域名請求, 從而實現跨域CORS訪問(有關CORS詳細介紹請耐心往下讀).
  • 能夠設置timeout 及 ontimeout, 方便設置超時時長和超時後續處理.

這裏就H5新增的FormData對象舉個例.

//可直接建立FormData實例
var data = new FormData();
data.append("name", "louis");
xhr.send(data);
//還能夠經過傳入表單DOM對象來建立FormData實例
var form = document.getElementById('form');
var data = new FormData(form);
data.append("password", "123456");
xhr.send(data);複製代碼

目前, 主流瀏覽器基本上都支持XHR2, 除了IE系列須要IE10及更高版本. 所以IE10如下是不支持XHR2的.

那麼問題來了, IE7, 8,9的用戶怎麼辦? 很遺憾, 這些用戶是比較尷尬的. 對於IE8,9而言, 只有一個閹割版的 XDomainRequest 可用,IE7則沒有. 估計IE7用戶只能哭暈在廁所了.

XDomainRequest

XDomainRequest 對象是IE8,9折騰出來的, 用於支持CORS請求非成熟的解決方案. 以致於IE10中直接移除了它, 並從新回到了 XMLHttpRequest 的懷抱.

XDomainRequest 僅可用於發送 GETPOST 請求. 以下即建立過程.

var xdr = new XDomainRequest();複製代碼

xdr具備以下屬性:

  • timeout
  • responseText

以下方法:

  • open: 只能接收Method,和url兩個參數. 只能發送異步請求.
  • send
  • abort

以下事件回調:

  • onprogress
  • ontimeout
  • onerror
  • onload

除了缺乏一些方法外, XDomainRequest 基本上就和 XMLHttpRequest 的使用方式保持一致.

必需要明確的是:

  • XDomainRequest 不支持跨域傳輸cookie.
  • 只能設置請求頭的Content-Type字段, 且不能訪問響應頭信息.

$.ajax

$.ajax是jquery對原生ajax的一次封裝. 經過封裝ajax, jquery抹平了不一樣版本瀏覽器異步http的差別性, 取而代之的是高度統一的api. jquery做爲js類庫時代的先驅, 對前端發展有着深遠的影響. 瞭解並熟悉其ajax方法, 不可謂不重要.

參數列表

$.ajax() 只有一個參數, 該參數爲key-value設置對象. 實際上, jq發送的全部ajax請求, 都是經過調用該ajax方法實現的. 它的詳細參數以下表:

序號參數類型描述
1acceptsPlainObject用於通知服務器該請求須要接收何種類型的返回結果. 若有必要, 推薦在 $.ajaxSetup()方法中設置一次.
2asyncBoolean默認爲true, 即異步.
3beforeSendFunction請求發送前的回調, 默認傳入參數jqXHR和settings. 函數內顯式返回false將取消本次請求.
4cacheBoolean請求是否開啓緩存, 默認爲true, 如不須要緩存請設置爲false. 不過, dataType爲"script"和"jsonp"時默認爲false.
5completeFunction請求完成後的回調(請求success 和 error以後均調用), 默認傳入參數jqXHR和textStatus(請求狀態, 取值爲 "success","notmodified","error","timeout","abort","parsererror"之一). 從jq1.5開始, complete能夠設置爲一個包含函數的數組. 如此每一個函數將依次被調用.
6contentsPlainObject一個以"{字符串/正則表達式}"配對的對象, 根據給定的內容類型, 解析請求的返回結果.
7contentTypeString編碼類型, 相對應於http請求頭域的"Content-Type"字段. 默認值爲"application/x-www-form-urlencoded; charset=UTF-8".
8contextObject設置ajax回調函數的上下文. 默認上下文爲ajax請求傳入的參數設置對象. 如設置爲document.body, 那麼全部ajax回調函數中將以body爲上下文.
9convertersPlainObject一個數據類型到數據類型轉換器的對象. 默認爲 {"* text": window.String, "text html": true, "text json": jQuery.parseJSON, "text xml": jQuery.parseXML} . 如設置converters:{"json jsonp": function(msg){}}
10crossDomainBoolean默認同域請求爲false, 跨域請求爲true.
11dataObject, Array發送到服務器的數據, 默認data爲鍵值對格式對象, 若data爲數組則按照traditional參數的值, 自動轉化爲一個同名的多值查詢字符串. 如{a:1,b:2}將轉換爲"&a=1&b=2".
12dataFilterFunction處理XMLHttpRequest原始響應數據的回調, 默認傳入data和type參數, data是Ajax返回的原始數據, type是調用$.ajax時提供的dataType參數
13dataTypeString預期服務器返回的數據類型, 可設置爲"xml","html","script","json","jsonp","text"之一, 其中設置爲"xml"或"text"類型時, 數據不會通過處理.
14errorFunction請求失敗時的回調函數, 默認傳入jqXHR(jq1.4之前爲原生xhr對象),textStatus(請求狀態,取值爲null,"timeout","error","abort" 或 "parsererror"),errorString(錯誤內容), 當一個HTTP錯誤發生時, errorThrown 接收HTTP狀態的文本部分,好比"Not Found"等. 從jq1.5開始, error能夠設置爲一個包含函數的數組. 如此每一個函數將依次被調用.注意: 跨域腳本和JSONP請求時error不被調用.
15globalBoolean表示是否觸發全局ajax事件, 默認爲true. 設爲false將再也不觸發ajaxStart,ajaxStop,ajaxSend,ajaxError等. 跨站腳本和jsonp請求, 該值自動設置爲false.
16headersPlainObject設置請求頭, 格式爲k-v鍵值對對象. 因爲該設置會在beforeSend函數被調用以前生效, 所以可在beforeSend函數內覆蓋該對象.
17ifModifiedBoolean只有上次請求響應改變時, 才容許請求成功. 它使用HTTP包的Last-Modified 頭信息判斷, 默認爲false. 若設置爲true, 且數據自從上次請求後沒有更改過就會報錯.
18isLocalBoolean運行當前環境設置爲"本地",默認爲false, 若設置爲true, 將影響請求發送時的協議.
19jsonpString顯式指定jsonp請求中的回調函數的名稱. 如jsonp:cb, jq會將cb代替callback, 以 "cb=?"傳給服務器. 從jq1.5開始, 若設置jsonp:false, 那麼須要明確設置jsonpCallback:"callbackName".
20jsonpCallbackString,Function爲jsonp請求指定一個回調函數名, 以取代jq自動生成的隨機函數名. 從jq1.5開始, 能夠將該屬性設置爲一個函數, 函數的返回值就是jsonpCallback的結果.
21mimeTypeString設置一個MIME類型, 以覆蓋xhr的MIM類型(jq1.5新增)
22passwordString設置認證請求中的密碼
23processDataBooleanjq的ajax方法默認會將傳入的data隱式轉換爲查詢字符串(如"&a=1&b=2"), 以配合 默認內容類型 "application/x-www-form-urlencoded", 若是不但願轉換請設置爲false. angular中想要禁用默認轉換, 須要重寫transformRequest方法.
24scriptCharsetString僅在"script"請求中使用(如跨域jsonp, dataType爲"script"類型). 顯式指定時, 請求中將在script標籤上設置charset屬性, 可在發現本地和遠程編碼不一致時使用.
25statusCodePlainObject一組http狀態碼和回調函數對應的鍵值對對象. 該對象以 {404:function(){}} 這種形式表示. 可用於根據不一樣的http狀態碼, 執行不一樣的回調.(jq1.5新增)
26timeoutNumber設置超時時間.
27traditionalBoolean是否按照默認方式序列化data對象, 默認值爲false.
28typeString能夠設置爲8種http method之一, jq中不區分大小寫.
29urlString請求的uri地址.
30usernameString設置認證請求中的用戶名
31xhrFunction在回調內建立並返回xhr對象
32xhrFieldsPlainObject鍵值對對象, 用於設置原生的xhr對象, 如可用來設置withCredentials:true(jq1.5.1新增)

支持promise

$.ajax() 方法返回jqXHR對象(jq1.5起), 若是使用的不是XMLHttpRequest對象時, 如jsonp請求, 返回的jqXHR對象將盡量模擬原生的xhr. 從jq1.5起, 返回的jqXHR對象實現了promise接口, 具備以下新方法.

新方法被替代的老方法(jq1.8起棄用)
done(function(data, textStatus, jqXHR) {})success
fail(function(jqXHR, textStatus, errorThrown) {})error
always(function(data or jqXHR, textStatus, jqXHR or errorThrown) {})complete

從jq1.6開始, done, fail, always按照FIFO隊列能夠分配多個回調.

使用轉換器

$.ajax() 的轉換器能夠將支持的數據類型映射到其它數據類型. 若是須要將自定義數據類型映射到已知的類型. 須要使用 contents 選項在響應的 "Content-Type" 和實際數據類型之間添加一個轉換函數.

$.ajaxSetup({
  contents: {
    myContentType: /myContentType/
  },
  converters: {
    "myContentType json": function(data) {
      //TODO something
      return newData;
    }
  }
});複製代碼

轉換一個支持的類型爲自定義類型, 而後再返回. 如 text—>myContentType—>json.

$.ajaxSetup({
  contents: {
    myContentType: /myContentType/
  },
  converters: {
    "text myContentType": true,
    "myContentType json": function(data) {
      //TODO something
      return newData;
    }
  }
});複製代碼

事件觸發順序

$.ajax()方法觸發的事件紛繁複雜, 有將近20個之多. 爲了囊括最多的事件, 這裏以一次成功的上傳請求爲例, 如下是它們的調用順序(請求出現錯誤時的順序, 請自行對應).

序號事件名稱是否全局事件是否能關閉默認形參
1$.ajaxPrefilter✔️❌function(options, originalOptions, jqXHR){}
2$(document).ajaxStar✔️✔️function(){}(只在當前無激活ajax時觸發)
3beforeSend❌-function(jqXHR, settings){}
4$(document).ajaxSend✔️✔️function(){}
5xhr.onloadstart--ProgressEvent
6xhr.upload.onloadstart--ProgressEvent
7xhr.upload.onprogress--ProgressEvent
8xhr.upload.onload--ProgressEvent
9xhr.upload.onloadend--ProgressEvent
10xhr.onprogress--ProgressEvent
11xhr.onload--ProgressEvent
12success(棄用)❌-function(data, textStatus, jqXHR){}
13$(document).ajaxSuccess✔️✔️function(event, jqXHR, options){}
14complete(棄用)❌-function(jqXHR, textStatus){}
15$(document).ajaxComplete✔️✔️function(event, jqXHR, textStatus)
16$(document).ajaxStop✔️✔️function(){}
17xhr.onloadend--ProgressEvent

從jq1.8起, 對於函數 ajaxStart, ajaxSend, ajaxSuccess, ajaxComplete, ajaxStop , 只能爲document對象綁定事件處理函數, 爲其餘元素綁定的事件處理函數不會起做用.

Axios

實際上, 若是你僅僅只是想要一個不錯的http庫, 相比於龐大臃腫的jquery, 短小精悍的Axios可能更加適合你. 緣由以下:

  • Axios支持node, jquery並不支持.
  • Axios基於promise語法, jq3.0纔開始全面支持.
  • Axios短小精悍, 更加適合http場景, jquery大而全, 加載較慢.
  • vue做者尤大放棄推薦vue-resource, 轉向推薦Axios. 如下爲尤大原話.

"最近團隊討論了一下, Ajax 自己跟 Vue 並無什麼須要特別整合的地方, 使用 fetch polyfill 或是 axios、superagent 等等均可以起到同等的效果, vue-resource 提供的價值和其維護成本相比並不划算, 因此決定在不久之後取消對 vue-resource 的官方推薦."

Axios大小僅12k, 目前最新版本號爲:

語法上Axios基本就和promise同樣, 在then方法中處理回調, 在catch方法中處理異常. 以下:

axios.get("https://api.github.com/users/louiszhai")
  .then(function(response){
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });複製代碼

除了get, 它還支持post, delete, head, put, patch, request請求. 具體使用攻略, 請戳這裏: axios .

如需在網頁上引入 Axios, 能夠連接CDN axios | Bootstrap中文網開源項目免費 CDN 服務 或者將其下載到本地.

Fetch

說到ajax, 就不得不說起fetch, 因爲篇幅較長, fetch已從本文中獨立出來, 請戳 Fetch進階指南 .

ajax跨域請求什麼是CORS

CORS是一個W3C(World Wide Web)標準, 全稱是跨域資源共享(Cross-origin resource sharing).它容許瀏覽器向跨域服務器, 發出異步http請求, 從而克服了ajax受同源策略的限制. 實際上, 瀏覽器不會攔截不合法的跨域請求, 而是攔截了他們的響應, 所以即便請求不合法, 不少時候, 服務器依然收到了請求.(Chrome和Firefox下https網站不容許發送http異步請求除外)

一般, 一次跨域訪問擁有以下流程:

移動端CORS兼容性

當前幾乎全部的桌面瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)均可經過名爲跨域資源共享的協議支持ajax跨域調用.

那麼移動端兼容性又如何呢? 請看下圖:

cors-mobile

可見, CORS的技術在IOS Safari7.1及Android webview2.3中就早已支持, 即便低版本下webview的canvas在使用跨域的video或圖片時會有問題, 也絲絕不影響CORS的在移動端的使用. 至此, 咱們就能夠放心大膽的去應用CORS了.

CORS有關的headers

1) HTTP Response Header(服務器提供):

  • Access-Control-Allow-Origin: 指定容許哪些源的網頁發送請求.

  • Access-Control-Allow-Credentials: 指定是否容許cookie發送.

  • Access-Control-Allow-Methods: 指定容許哪些請求方法.

  • Access-Control-Allow-Headers: 指定容許哪些常規的頭域字段, 好比說 Content-Type.

  • Access-Control-Expose-Headers: 指定容許哪些額外的頭域字段, 好比說 X-Custom-Header.

    該字段可省略. CORS請求時, xhr.getResponseHeader() 方法默認只能獲取6個基本字段: Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma . 若是須要獲取其餘字段, 就須要在Access-Control-Expose-Headers 中指定. 如上, 這樣xhr.getResponseHeader('X-Custom-Header') 才能返回X-Custom-Header字段的值.(該部分摘自阮一峯老師博客)

  • Access-Control-Max-Age: 指定preflight OPTIONS請求的有效期, 單位爲秒.

2) HTTP Request Header(瀏覽器OPTIONS請求默認自帶):

  • Access-Control-Request-Method: 告知服務器,瀏覽器將發送哪一種請求, 好比說POST.
  • Access-Control-Request-Headers: 告知服務器, 瀏覽器將包含哪些額外的頭域字段.

3) 如下全部的header name 是被拒絕的:

  • Accept-Charset
  • Accept-Encoding
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Connection
  • Content-Length
  • Cookie
  • Cookie2
  • Date
  • DNT
  • Expect
  • Host
  • Keep-Alive
  • Origin
  • Referer
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade
  • Via
  • 包含以Proxy-Sec- 開頭的header name

CORS請求

CORS請求分爲兩種, ① 簡單請求; ② 非簡單請求.

知足以下兩個條件即是簡單請求, 反之則爲非簡單請求.(CORS請求部分摘自阮一峯老師博客)

1) 請求是如下三種之一:

  • HEAD
  • GET
  • POST

2) http頭域不超出如下幾種字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type字段限三個值 application/x-www-form-urlencodedmultipart/form-datatext/plain

對於簡單請求, 瀏覽器將發送一次http請求, 同時在Request頭域中增長 Origin 字段, 用來標示請求發起的源, 服務器根據這個源採起不一樣的響應策略. 若服務器認爲該請求合法, 那麼須要往返回的 HTTP Response 中添加 Access-Control-* 等字段.( Access-Control-* 相關字段解析請閱讀我以前寫的CORS 跨域訪問 )

對於非簡單請求, 好比Method爲POST且Content-Type值爲 application/json 的請求或者Method爲 PUTDELETE 的請求, 瀏覽器將發送兩次http請求. 第一次爲preflight預檢(Method: OPTIONS),主要驗證來源是否合法. 值得注意的是:OPTION請求響應頭一樣須要包含 Access-Control-* 字段等. 第二次纔是真正的HTTP請求. 因此服務器必須處理OPTIONS應答(一般須要返回20X的狀態碼, 不然xhr.onerror事件將被觸發).

以上請求流程圖爲:

HTML啓用CORS

http-equiv 至關於http的響應頭, 它迴應給瀏覽器一些有用的信息,以幫助正確和精確地顯示網頁內容. 以下html將容許任意域名下的網頁跨域訪問.

<meta http-equiv="Access-Control-Allow-Origin" content="*">複製代碼

圖片啓用CORS

一般, 圖片容許跨域訪問, 也能夠在canvas中使用跨域的圖片, 但這樣作會污染畫布, 一旦畫布受污染, 將沒法讀取其數據. 好比沒法調用 toBlob(), toDataURL() 或 getImageData()方法. 瀏覽器的這種安全機制規避了未經許可的遠程服務器圖片被濫用的風險.(該部份內容摘自 啓用了 CORS 的圖片 - HTML(超文本標記語言) | MDN)

所以如需在canvas中使用跨域的圖片資源, 請參考以下apache配置片斷(來自HTML5 Boilerplate Apache server configs).

<IfModule mod_setenvif.c>
    <IfModule mod_headers.c>
        <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
            SetEnvIf Origin ":" IS_CORS
            Header set Access-Control-Allow-Origin "*" env=IS_CORS
        </FilesMatch>
    </IfModule>
</IfModule>複製代碼

ajax文件上傳

ajax實現文件上傳很是簡單, 這裏我選取原生js, jq, angular 分別來比較下, 並順便聊聊使用它們時的注意事項.(ajax文件上傳的代碼已上傳至github, 請戳這裏預覽效果: ajax 文件上傳 demo | louis)

1) 爲了上傳文件, 咱們得先選中一個文件. 一個type爲file的input框就夠了.

<input id="input" type="file">複製代碼

2) 而後用FormData對象包裹📦選中的文件.

var input = document.getElementById("input"),
    formData = new FormData();
formData.append("file",input.files[0]);//key能夠隨意定義,只要後臺能理解就行複製代碼

3) 定義上傳的URL, 以及方法. github上我搭建了一個 node-webserver, 根據須要能夠自行克隆下來npm start後即可調試本篇代碼.

var url = "http://localhost:10108/test",
    method = "POST";複製代碼

js文件上傳

4.1) 封裝一個用於發送ajax請求的方法.

function ajax(url, method, data){
  var xhr = null;
  if(window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) { 
        alert("您的瀏覽器暫不支持Ajax!");
      }
    }
  }
  xhr.onerror = function(e){
    console.log(e);
  }
  xhr.open(method, url);
  try{
    setTimeout(function(){
      xhr.send(data);
    });
  }catch(e){
    console.log('error:',e);
  }
  return xhr;
}複製代碼

4.2) 上傳文件並綁定事件.

var xhr = ajax(url, method, formData);
xhr.upload.onprogress = function(e){
  console.log("upload progress:", e.loaded/e.total*100 + "%");
};
xhr.upload.onload = function(){
  console.log("upload onload.");
};
xhr.onload = function(){
  console.log("onload.");
}複製代碼

上傳結果以下所示:

js file upload

fetch上傳

5) fetch只要發送一個post請求, 而且body屬性設置爲formData便可. 遺憾的是, fetch沒法跟蹤上傳的進度信息.

fetch(url, {
  method: method,
  body: formData
  }).then(function(res){
  console.log(res);
  }).catch(function(e){
  console.log(e);
});複製代碼

jquery文件上傳

jq提供了各式各樣的上傳插件, 其原理都是利用jq自身的ajax方法.

6) jq的ajax提供了xhr屬性用於自定義各類事件.

$.ajax({
  type: method,
  url: url,
  data: formData,
  processData : false,
  contentType : false ,//必須false纔會自動加上正確的Content-Type
  xhr: function(){
    var xhr = $.ajaxSettings.xhr();//實際上就是return new window.XMLHttpRequest()對象
    if(xhr.upload) {
      xhr.upload.addEventListener("progress", function(e){
        console.log("jq upload progress:", e.loaded/e.total*100 + "%");
      }, false);
      xhr.upload.addEventListener("load", function(){
        console.log("jq upload onload.");
      });
      xhr.addEventListener("load", function(){
        console.log("jq onload.");
      });
      return xhr;
    }
  }
});複製代碼

jq上傳結果以下所示:

jq file upload

有關jq ajax更多的api, 請參考中文文檔 jQuery.ajax() | jQuery API 中文文檔 .

angular文件上傳

7.1) angular提供了$http方法用於發送http請求, 該方法返回一個promise對象.

$http({
  method: method,
  url: url,
  data: formData,
}).success(function(res) {
  console.log(res);
}).error(function(err, status) {
  console.log(err);
});複製代碼

angular文件上傳的代碼已上傳至github, 請戳這裏預覽效果: angular 文件上傳 demo | louis.

低版本angular中文件上傳的功能並不完整, 直到angular1.5.5纔在$http中加入了eventHandler和uploadEventHandlers等方法, 使得它支持上傳進度信息. 以下:

$http({
  method: method,
  url: url,
  eventHandlers: {
    progress: function(c) {//下載進度
      console.log('Progress -> ' + c);
    }
  },
  uploadEventHandlers: {
    progress: function(e) {//上傳進度
      console.log('UploadProgress -> ' + e);
    }
  },
  data: formData,
}).success(function(res) {
  console.log(res);
}).error(function(err, status) {
  console.log(err);
});複製代碼

angular1.5.5如下低版本中, 請參考成熟的實現方案 angular-file-upload 以及它提供的demo Simple example .

ajax請求二進制文件FileReader

處理二進制文件主要使用的是H5的FileReader.

PC支持性以下:

IEEdgeFirefoxChromeSafariOpera
10123.66611.5

Mobile支持性以下:

IOS SafariOpera MiniAndroid BrowserChrome/AndroidUC/Android
7.1-45311

如下是其API:

屬性/方法名稱描述
error表示讀取文件期間發生的錯誤.
readyState表示讀取文件的狀態.默認有三個值:0表示文件尚未加載;1表示文件正在讀取;2表示文件讀取完成.
result讀取的文件內容.
abort()取消文件讀取操做, 此時readyState屬性將置爲2.
readAsArrayBuffer()讀取文件(或blob對象)爲類型化數組(ArrayBuffer), 類型化數組容許開發者以數組下標的方式, 直接操做內存, 因爲數據以二進制形式傳遞, 效率很是高.
readAsBinaryString()讀取文件(或blob對象)爲二進制字符串, 該方法已移出標準api, 請謹慎使用.
readAsDataURL()讀取文件(或blob對象)爲base64編碼的URL字符串, 與window.URL.createObjectURL方法效果相似.
readAsText()讀取文件(或blob對象)爲文本字符串.
onload()文件讀取完成時的事件回調, 默認傳入event事件對象. 該回調內, 可經過this.result 或 event.target.result獲取讀取的文件內容.

ajax請求二進制圖片並預覽

var xhr = new XMLHttpRequest(),
    url = "https://user-gold-cdn.xitu.io/2017/3/15/c6eacd7c2f4307f34cd45e93885d1cb6.png";
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.onload = function(){
  if(this.status == 200){
    var blob = this.response;
    var img = document.createElement("img");
    //方案一
    img.src = window.URL.createObjectURL(blob);//這裏blob依然佔據着內存
    img.onload = function() {
      window.URL.revokeObjectURL(img.src);//釋放內存
    };
    //方案二
    /*var reader = new FileReader();
    reader.readAsDataURL(blob);//FileReader將返回base64編碼的data-uri對象
    reader.onload = function(){
      img.src = this.result;
    }*/
    //方案三
    //img.src = url;//最簡單方法
    document.body.appendChild(img);
  }
}
xhr.send();複製代碼

ajax請求二進制文本並展現

var xhr = new XMLHttpRequest();
xhr.open("GET","http://localhost:8080/Information/download.jsp?data=node-fetch.js");
xhr.responseType = "blob";
xhr.onload = function(){
  if(this.status == 200){
    var blob = this.response;
    var reader = new FileReader();
    reader.readAsBinaryString(blob);//該方法已被移出標準api,建議使用reader.readAsText(blob);
    reader.onload=function(){
      document.body.innerHTML = "<div>" + this.result + "</div>";
    }
  }
}
xhr.send();複製代碼

有關二進制文件的讀取, 請移步這篇博客 HTML5新特性之文件和二進制數據的操做 .

如何等待多個ajax請求完成

原生js可使用ES6新增的Promise. ES6的Promise基於 Promises/A+ 規範(該部分 Fetch入門指南 一文也有說起).

這裏先提供一個解析responses的函數.

function todo(responses){
  responses.forEach(function(response){
    response.json().then(function(res){
      console.log(res);
    });
  });
}複製代碼

原生js使用 Promise.all 方法. 以下:

var p1 = fetch("http://localhost:10108/test1"),
    p2 = fetch("http://localhost:10108/test2");
Promise.all([p1, p2]).then(function(responses){
  todo(responses);
  //TODO do somethings
});
//"test1"
//"test2"複製代碼

jquery可使用$.when方法. 該方法接受一個或多個Deferred對象做爲參數, 只有所有成功才調用resolved狀態的回調函數, 但只要其中有一個失敗,就調用rejected狀態的回調函數. 其實, jq的Deferred是基於 Promises/A規範實現, 但並不是徹底遵循. (傳送門: jQuery 中的 Deferred 和 Promises (2) ).

var p1 = $.ajax("http://localhost:10108/test1"),
    p2 = $.ajax("http://localhost:10108/test2");
$.when(p1, p2).then(function(res1, res2){
  console.log(res1);//["test1", "success", Object]
  console.log(res2);//["test2", "success", Object]
  //TODO do somethings
});複製代碼

如上, $.when默認返回一個jqXHR對象, 能夠直接進行鏈式調用. then方法的回調中默認傳入相應的請求結果, 每一個請求結果的都是數組, 數組中依次是responseText, 請求狀態, 請求的jqXHR對象.

angular中能夠藉助 $q.all() 來實現. 別忘了, $q 須要在controller中注入. 此外, $q 相關講解可參考 AngularJS: ng.$qAngular $q service學習筆記 .

var p1 = fetch("http://localhost:10108/test1"),
    p2 = fetch("http://localhost:10108/test2");
$q.all([p1, p2]).then(function(responses){
  todo(responses);
  //TODO do somethings
});
//"test1"
//"test2"複製代碼

$q.all() 實際上就是對 Promise.all 的封裝.

ajax與history的兼容

ajax的一大痛點就是沒法支持瀏覽器前進和後退操做. 所以早期的Gmail 採用 iframe, 來模擬ajax的前進和後退.

現在, H5普及, pjax大行其道. pajax 就是 ajax+history.pushState 組合的一種技術. 使用它即可以無刷新經過瀏覽器前進和後退來改變頁面內容.

先看下兼容性.

IEEdgeFirefoxChromeSafariOperaiOS SafariAndroid BrowserChrome for Android
pushState/replaceState101245611.57.14.353
history.state10418611.5

可見IE8,9並不能使用 H5的history. 須要使用墊片 HTML5 History API expansion for browsers not supporting pushState, replaceState .

pjax

pjax簡單易用, 僅須要以下三個api:

  • history.pushState(obj, title, url) 表示往頁面history末尾新增一個歷史項(history entry), 此時history.length會+1.
  • history.replaceState(obj, title, url) 表示替換當前歷史項爲新的歷史項. 此時history.length保持不變.
  • window.onpopstate 僅在瀏覽器前進和後退時觸發(history.go(1), history.back() 及location.href="xxx" 均會觸發), 此時可在history.state中拿到剛剛塞進去的state, 即obj對象(其餘數據類型亦可).

咱們注意到, 首次進入一個頁面, 此時 history.length 值爲1, history.state 爲空. 以下:

history.state

1) 爲了在onpopstate事件回調中每次都能拿到 history.state , 此時須要在頁面載入完成後, 自動替換下當前url.

history.replaceState("init", title, "xxx.html?state=0");複製代碼

2) 每次發送ajax請求時, 在請求完成後, 調用以下, 從而實現瀏覽器history往前進.

history.pushState("ajax請求相關參數", title, "xxx.html?state=標識符");複製代碼

3) 瀏覽器前進和後退時, popstate 事件會自動觸發, 此時咱們手動取出 history.state , 構建參數並從新發送ajax請求或者直接取用state值, 從而實現無刷新還原頁面.

window.addEventListener("popstate", function(e) {
    var currentState = history.state;
    //TODO 拼接ajax請求參數並從新發送ajax請求, 從而回到歷史頁面
      //TODO 或者從state中拿到關鍵值直接還原歷史頁面
});複製代碼

popstate 事件觸發時, 默認會傳入 PopStateEvent 事件對象. 該對象具備以下屬性.

PopStateEvent

若有不懂, 更詳細講解請移步 : ajax與HTML5 history pushState/replaceState實例 « 張鑫旭-鑫空間-鑫生活 .

ajax緩存處理

js中的http緩存沒有開關, 受制於瀏覽器http緩存策略. 原生xhr請求中, 可經過以下設置關閉緩存.

xhr.setRequestHeader("If-Modified-Since","0");
xhr.setRequestHeader("Cache-Control","no-cache");
//或者 URL 參數後加上  "?timestamp=" + new Date().getTime()複製代碼

jquery的http緩存是否開啓可經過在settings中指定cache.

$.ajax({
  url : 'url',
  dataType : "xml",
  cache: true,//true表示緩存開啓, false表示緩存不開啓
  success : function(xml, status){    
  }
});複製代碼

同時jquery還能夠全局設置是否緩存. 以下將全局關閉ajax緩存.

$.ajaxSetup({cache:false});複製代碼

除此以外, 調試過程當中出現的瀏覽器緩存尤其可惡. 建議開啓隱私瀏覽器或者勾選☑️控制檯的 Disable cache 選項. (這裏以Chrome舉例, 其餘瀏覽器相似)

PopStateEvent

ajax的錯誤處理

前面已經提過, 一般只要是ajax請求收到了http狀態碼, 便不會進入到錯誤捕獲裏.(Chrome中407響應頭除外)

實際上, $.ajax 方法略有區別, jquery的ajax方法還會在類型解析出錯時觸發error回調. 最多見的即是: dataType設置爲json, 可是返回的data並不是json格式, 此時 $.ajax 的error回調便會觸發.

ajax調試技巧

有關調試, 若是接口只是作小部分修改. 那麼可使用charles(Mac) 或者fiddler(Windows), 作代理, 將請求的資源替換爲本地文件, 或者使用其斷點功能, 直接編輯response.

若是是新增接口的調試, 能夠本地搭建node服務. 利用hosts文件配置dns + nginx將http請求轉發到本地node服務器. 簡易的node調試服務器可參考個人 node-webserver . 以下舉一個栗子🌰:

hosts+nginx+node-webserver

假設咱們要調試的是 www.test.com 的GET接口. 如下全部步驟以Mac爲例, 其餘系統, 請自行搜索🔍文件路徑.

1) hosts配置.

sudo vim /etc/hosts
#新增一行 127.0.0.1 www.test.com複製代碼

2) nginx 配置

brew install nginx #安裝
#安裝成功後進入目標目錄
cd /usr/local/etc/nginx/
cd servers #默認配置入口爲nginx.conf.同時servers目錄下*.conf文件已自動加入到配置文件列表中
vim test.conf
#粘貼以下內容
server {
  listen       80;
  server_name  www.test.com;
  index index.html;
  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
      root   html;
  }
  location / {
    proxy_pass http://localhost:10108/;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header        X-Read-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}
#:wq保存並退出
#啓動nginx
sudo nginx -s reload #若是啓動了只需重啓便可
sudo nginx #若是沒有啓動,便啓動之複製代碼

3) node-webServer 配置

參考 node-webserver . 啓動服務前只需更改index.js, 在第9行後插入以下內容:

'get': {
  '/': {
      getKey : 'Welcome to Simple Node WebServer!'
  },
  '接口api': '你的response內容'//插入的代碼                               
},複製代碼

如需在nginx中配置CORS, 請看這裏: Nginx經過CORS實現跨域.

編碼問題

XMLHttpRequest 返回的數據默認的字符編碼是utf-8, post方法提交數據默認的字符編碼也是utf-8. 若頁面編碼爲gbk等中文編碼, 那麼就會產生亂碼.

後端接口測試技巧

一般, 若是後端接口開發OK了, 前端同窗須要經過一些手段來確認接口是能正常訪問的.

使用命令測試OPTIONS請求

curl -I -X OPTIONS -H "Origin: http://example.com" http://localhost:10108/
# response
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/json;charset=UTF-8
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-requested-with,Content-Type
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Origin: http://example.com
Access-Control-Max-Age: 3600
Server: Node WebServer
Website: https://github.com/Louiszhai/node-webserver
Date: Fri, 21 Oct 2016 09:00:40 GMT
Connection: keep-alive
Transfer-Encoding: chunked複製代碼

以上, http狀態碼爲200, 表示容許OPTIONS請求.

GET, POST 請求與GET相似, 其餘請求亦然.

curl -I -X GET -H "Origin: http://example.com" http://localhost:10108/
#HTTP/1.1 200 OK
curl -I -X POST -H "Origin: http://example.com" http://localhost:10108/test
#HTTP/1.1 200 OK複製代碼

postman

除此以外, 咱們還能夠經過chrome的postman擴展進行測試. 請看postman素潔的界面:

postman支持全部類型的http請求, 因爲其向chrome申請了cookie訪問權限及全部http(s)網站的訪問權限. 所以能夠放心使用它進行各類網站api的測試.

同時, 強烈建議閱讀本文的你升級postman的使用技巧, 這裏有篇: 基於Postman的API自動化測試 , 拿走不謝.

ajax移動端兼容性

移動端的支持性比較弱, 使用需謹慎. 看錶.

IOS SafariOpera MiniAndroid BrowserAndroid ChromeAndroid UC
XMLHttpRequest8.4-4.4.45311(part)
fetch--5253-

本篇爲ajax而生, 通篇介紹 XMLHTTPRequest 相關的知識, 力求簡明, 本欲爲梳理知識, 爲讀者答疑解惑, 但因本人理解所限, 不免有所侷限, 但願正在閱讀的你取其精華去其糟粕. 謝謝.

本文就討論這麼多內容,你們有什麼問題或好的想法歡迎在下方參與留言和評論.

本文做者: louis

本文連接: louiszhai.github.io/2016/11/02/…

參考文章

相關文章
相關標籤/搜索