AJAX

瀏覽器與服務器之間,采用HTTP協議通信。用戶在瀏覽器地址欄鍵入一個網址,或者通過網頁表單向服務器提交內容,這時瀏覽器就會向服務器發出HTTP請求。

1999年,微軟公司發布IE瀏覽器5.0版,第一次引入新功能:允許JavaScript腳本向服務器發起HTTP請求。這個功能當時并沒有引起注意,直到2004年Gmail發布和2005年Google Map發布,才引起廣泛重視。2005年2月,AJAX這個詞第一次正式提出,指圍繞這個功能進行開發的一整套做法。從此,AJAX成為腳本發起HTTP通信的代名詞,W3C也在2006年發布了它的國際標準。

具體來說,AJAX包括以下幾個步驟。

  1. 創建AJAX對象
  2. 發出HTTP請求
  3. 接收服務器傳回的數據
  4. 更新網頁數據

概括起來,就是一句話,AJAX通過原生的XMLHttpRequest對象發出HTTP請求,得到服務器返回的數據后,再進行處理。

AJAX可以是同步請求,也可以是異步請求。但是,大多數情況下,特指異步請求。因為同步的Ajax請求,對瀏覽器有“堵塞效應”。

XMLHttpRequest對象

XMLHttpRequest對象用來在瀏覽器與服務器之間傳送數據。

var ajax = new XMLHttpRequest();
ajax.open('GET', 'http://www.example.com/page.php', true);

上面代碼向指定的服務器網址,發出GET請求。

然后,AJAX指定回調函數,監聽通信狀態(readyState屬性)的變化。

ajax.onreadystatechange = handleStateChange;

一旦拿到服務器返回的數據,AJAX不會刷新整個網頁,而是只更新相關部分,從而不打斷用戶正在做的事情。

注意,AJAX只能向同源網址(協議、域名、端口都相同)發出HTTP請求,如果發出跨源請求,就會報錯(詳見《同源政策》和《CORS機制》兩節)。

雖然名字里面有XML,但是實際上,XMLHttpRequest可以報送各種數據,包括字符串和二進制,而且除了HTTP,它還支持通過其他協議傳送(比如File和FTP)。

下面是XMLHttpRequest對象的典型用法。

var xhr = new XMLHttpRequest();

// 指定通信過程中狀態改變時的回調函數
xhr.onreadystatechange = function(){
  // 通信成功時,狀態值為4
  if (xhr.readyState === 4){
    if (xhr.status === 200){
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.onerror = function (e) {
  console.error(xhr.statusText);
};

// open方式用于指定HTTP動詞、請求的網址、是否異步
xhr.open('GET', '/endpoint', true);

// 發送HTTP請求
xhr.send(null);

open方法的第三個參數是一個布爾值,表示是否為異步請求。如果設為false,就表示這個請求是同步的,下面是一個例子。

var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false);
request.send(null);

if (request.status === 200) {
  console.log(request.responseText);
}

XMLHttpRequest實例的屬性

readyState

readyState是一個只讀屬性,用一個整數和對應的常量,表示XMLHttpRequest請求當前所處的狀態。

  • 0,對應常量UNSENT,表示XMLHttpRequest實例已經生成,但是open()方法還沒有被調用。
  • 1,對應常量OPENED,表示send()方法還沒有被調用,仍然可以使用setRequestHeader(),設定HTTP請求的頭信息。
  • 2,對應常量HEADERS_RECEIVED,表示send()方法已經執行,并且頭信息和狀態碼已經收到。
  • 3,對應常量LOADING,表示正在接收服務器傳來的body部分的數據,如果responseType屬性是text或者空字符串,responseText就會包含已經收到的部分信息。
  • 4,對應常量DONE,表示服務器數據已經完全接收,或者本次接收已經失敗了。

在通信過程中,每當發生狀態變化的時候,readyState屬性的值就會發生改變。這個值每一次變化,都會觸發readyStateChange事件。

if (ajax.readyState == 4) {
  // Handle the response.
} else {
 // Show the 'Loading...' message or do nothing.
}

上面代碼表示,只有readyState變為4時,才算確認請求已經成功,其他值都表示請求還在進行中。

onreadystatechange

onreadystatechange屬性指向一個回調函數,當readystatechange事件發生的時候,這個回調函數就會調用,并且XMLHttpRequest實例的readyState屬性也會發生變化。

另外,如果使用abort()方法,終止XMLHttpRequest請求,onreadystatechange回調函數也會被調用。

var xmlhttp = new XMLHttpRequest();
xmlhttp.open( 'GET', 'http://example.com' , true );
xmlhttp.onreadystatechange = function () {
  if ( XMLHttpRequest.DONE != xmlhttp.readyState ) {
    return;
  }
  if ( 200 != xmlhttp.status ) {
    return;
  }
  console.log( xmlhttp.responseText );
};
xmlhttp.send();

response

response屬性為只讀,返回接收到的數據體(即body部分)。它的類型可以是ArrayBuffer、Blob、Document、JSON對象、或者一個字符串,這由XMLHttpRequest.responseType屬性的值決定。

如果本次請求沒有成功或者數據不完整,該屬性就會等于null

responseType

responseType屬性用來指定服務器返回數據(xhr.response)的類型。

  • ”“:字符串(默認值)
  • “arraybuffer”:ArrayBuffer對象
  • “blob”:Blob對象
  • “document”:Document對象
  • “json”:JSON對象
  • “text”:字符串

text類型適合大多數情況,而且直接處理文本也比較方便,document類型適合返回XML文檔的情況,blob類型適合讀取二進制數據,比如圖片文件。

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 = new Blob([this.response], {type: 'image/png'});
    // 或者
    var blob = oReq.response;
  }
};

xhr.send();

如果將這個屬性設為ArrayBuffer,就可以按照數組的方式處理二進制數據。

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);
  for (var i = 0, len = binStr.length; i < len; ++i) {
  // var byte = uInt8Array[i];
  }
};

xhr.send();

如果將這個屬性設為“json”,支持JSON的瀏覽器(Firefox>9,chrome>30),就會自動對返回數據調用JSON.parse()方法。也就是說,你從xhr.response屬性(注意,不是xhr.responseText屬性)得到的不是文本,而是一個JSON對象。

XHR2支持Ajax的返回類型為文檔,即xhr.responseType=”document” 。這意味著,對于那些打開CORS的網站,我們可以直接用Ajax抓取網頁,然后不用解析HTML字符串,直接對XHR回應進行DOM操作。

responseText

responseText屬性返回從服務器接收到的字符串,該屬性為只讀。如果本次請求沒有成功或者數據不完整,該屬性就會等于null

如果服務器返回的數據格式是JSON,就可以使用responseText屬性。

var data = ajax.responseText;
data = JSON.parse(data);

responseXML

responseXML屬性返回從服務器接收到的Document對象,該屬性為只讀。如果本次請求沒有成功,或者數據不完整,或者不能被解析為XML或HTML,該屬性等于null

返回的數據會被直接解析為DOM對象。

/* 返回的XML文件如下
  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
  <book>
      <chapter id="1">(Re-)Introducing JavaScript</chapter>
      <chapter id="2">JavaScript in Action</chapter>
  </book>
*/

var data = ajax.responseXML;
var chapters = data.getElementsByTagName('chapter');

如果服務器返回的數據,沒有明示Content-Type頭信息等于text/xml,可以使用overrideMimeType()方法,指定XMLHttpRequest對象將返回的數據解析為XML。

status

status屬性為只讀屬性,表示本次請求所得到的HTTP狀態碼,它是一個整數。一般來說,如果通信成功的話,這個狀態碼是200。

  • 200, OK,訪問正常
  • 301, Moved Permanently,永久移動
  • 302, Move temporarily,暫時移動
  • 304, Not Modified,未修改
  • 307, Temporary Redirect,暫時重定向
  • 401, Unauthorized,未授權
  • 403, Forbidden,禁止訪問
  • 404, Not Found,未發現指定網址
  • 500, Internal Server Error,服務器發生錯誤

基本上,只有2xx和304的狀態碼,表示服務器返回是正常狀態。

if (ajax.readyState == 4) {
  if ( (ajax.status >= 200 && ajax.status < 300)
    || (ajax.status == 304) ) {
    // Handle the response.
  } else {
    // Status error!
  }
}

statusText

statusText屬性為只讀屬性,返回一個字符串,表示服務器發送的狀態提示。不同于status屬性,該屬性包含整個狀態信息,比如”200 OK“。

timeout

timeout屬性等于一個整數,表示多少毫秒后,如果請求仍然沒有得到結果,就會自動終止。如果該屬性等于0,就表示沒有時間限制。

  var xhr = new XMLHttpRequest();
  xhr.ontimeout = function () {
    console.error("The request for " + url + " timed out.");
  };
  xhr.onload = function() {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        callback.apply(xhr, args);
      } else {
        console.error(xhr.statusText);
      }
    }
  };
  xhr.open("GET", url, true);
  xhr.timeout = timeout;
  xhr.send(null);
}

事件監聽接口

XMLHttpRequest第一版,只能對onreadystatechange這一個事件指定回調函數。該事件對所有情況作出響應。 XMLHttpRequest第二版允許對更多的事件指定回調函數。

  • onloadstart 請求發出
  • onprogress 正在發送和加載數據
  • onabort 請求被中止,比如用戶調用了abort()方法
  • onerror 請求失敗
  • onload 請求成功完成
  • ontimeout 用戶指定的時限到期,請求還未完成
  • onloadend 請求完成,不管成果或失敗
xhr.onload = function() {
 var responseText = xhr.responseText;
 console.log(responseText);
 // process the response.
};

xhr.onerror = function() {
  console.log('There was an error!');
};

注意,如果發生網絡錯誤(比如服務器無法連通),onerror事件無法獲取報錯信息,所以只能顯示報錯。

withCredentials

withCredentials屬性是一個布爾值,表示跨域請求時,用戶信息(比如Cookie和認證的HTTP頭信息)是否會包含在請求之中,默認為false。即向[example.com](http://example.com/)發出跨域請求時,不會發送[example.com](http://example.com/)設置在本機上的Cookie(如果有的話)。

如果你需要通過跨域AJAX發送Cookie,需要打開withCredentials

xhr.withCredentials = true;

為了讓這個屬性生效,服務器必須顯式返回Access-Control-Allow-Credentials這個頭信息。

Access-Control-Allow-Credentials: true

.withCredentials屬性打開的話,不僅會發送Cookie,還會設置遠程主機指定的Cookie。注意,此時你的腳本還是遵守同源政策,無法 從document.cookie或者HTTP回應的頭信息之中,讀取這些Cookie。

XMLHttpRequest實例的方法

abort()

abort方法用來終止已經發出的HTTP請求。

ajax.open('GET', 'http://www.example.com/page.php', true);
var ajaxAbortTimer = setTimeout(function() {
  if (ajax) {
    ajax.abort();
    ajax = null;
  }
}, 5000);

上面代碼在發出5秒之后,終止一個AJAX請求。

getAllResponseHeaders()

getAllResponseHeaders方法返回服務器發來的所有HTTP頭信息。格式為字符串,每個頭信息之間使用CRLF分隔,如果沒有受到服務器回應,該屬性返回null

getResponseHeader()

getResponseHeader方法返回HTTP頭信息指定字段的值,如果還沒有收到服務器回應或者指定字段不存在,則該屬性為null

function getHeaderTime () {
  console.log(this.getResponseHeader("Last-Modified"));
}

var oReq = new XMLHttpRequest();
oReq.open("HEAD", "yourpage.html");
oReq.onload = getHeaderTime;
oReq.send();

如果有多個字段同名,則它們的值會被連接為一個字符串,每個字段之間使用“逗號+空格”分隔。

open()

XMLHttpRequest對象的open方法用于指定發送HTTP請求的參數,它的使用格式如下,一共可以接受五個參數。

void open(
   string method,
   string url,
   optional boolean async,
   optional string user,
   optional string password
);

  • method:表示HTTP動詞,比如“GET”、“POST”、“PUT”和“DELETE”。
  • url: 表示請求發送的網址。
  • async: 格式為布爾值,默認為true,表示請求是否為異步。如果設為false,則send()方法只有等到收到服務器返回的結果,才會有返回值。
  • user:表示用于認證的用戶名,默認為空字符串。
  • password:表示用于認證的密碼,默認為空字符串。

如果對使用過open()方法的請求,再次使用這個方法,等同于調用abort()

下面發送POST請求的例子。

xhr.open('POST', encodeURI('someURL'));
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {};
xhr.send(encodeURI('dataString'));

上面方法中,open方法向指定URL發出POST請求,send方法送出實際的數據。

下面是一個同步AJAX請求的例子。

var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false);
request.send(null);

if (request.status === 200) {
  console.log(request.responseText);
}

send()

send方法用于實際發出HTTP請求。如果不帶參數,就表示HTTP請求只包含頭信息,也就是只有一個URL,典型例子就是GET請求;如果帶有參數,就表示除了頭信息,還帶有包含具體數據的信息體,典型例子就是POST請求。

ajax.open('GET'
  , 'http://www.example.com/somepage.php?id=' + encodeURIComponent(id)
  , true
);

// 等同于
var data = 'id=' + encodeURIComponent(id));
ajax.open('GET', 'http://www.example.com/somepage.php', true);
ajax.send(data);

上面代碼中,GET請求的參數,可以作為查詢字符串附加在URL后面,也可以作為send方法的參數。

下面是發送POST請求的例子。

var data = 'email='
  + encodeURIComponent(email)
  + '&password='
  + encodeURIComponent(password);
ajax.open('POST', 'http://www.example.com/somepage.php', true);
ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajax.send(data);

如果請求是異步的(默認為異步),該方法在發出請求后會立即返回。如果請求為同步,該方法只有等到收到服務器回應后,才會返回。

注意,所有XMLHttpRequest的監聽事件,都必須在send()方法調用之前設定。

send方法的參數就是發送的數據。多種格式的數據,都可以作為它的參數。

void send();
void send(ArrayBufferView data);
void send(Blob data);
void send(Document data);
void send(String data);
void send(FormData data);

如果發送Document數據,在發送之前,數據會先被串行化。

發送二進制數據,最好使用ArrayBufferViewBlob對象,這使得通過Ajax上傳文件成為可能。

下面是一個上傳ArrayBuffer對象的例子。

function sendArrayBuffer() {
  var xhr = new XMLHttpRequest();
  var uInt8Array = new Uint8Array([1, 2, 3]);

  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };
  xhr.send(uInt8Array.buffer);
}

FormData類型可以用于構造表單數據。

var formData = new FormData();

formData.append('username', '張三');
formData.append('email', 'zhangsan@example.com');
formData.append('birthDate', 1940);

var xhr = new XMLHttpRequest();
xhr.open("POST", "/register");
xhr.send(formData);

上面的代碼構造了一個formData對象,然后使用send方法發送。它的效果與點擊下面表單的submit按鈕是一樣的。

<form id='registration' name='registration' action='/register'>
    <input type='text' name='username' value='張三'>
    <input type='email' name='email' value='zhangsan@example.com'>
    <input type='number' name='birthDate' value='1940'>
    <input type='submit' onclick='return sendForm(this.form);'>
</form>

FormData也可以將現有表單構造生成。

var formElement = document.querySelector("form");
var request = new XMLHttpRequest();
request.open("POST", "submitform.php");
request.send(new FormData(formElement));

FormData對象還可以對現有表單添加數據,這為我們操作表單提供了極大的靈活性。

function sendForm(form) {
    var formData = new FormData(form);
    formData.append('csrf', 'e69a18d7db1286040586e6da1950128c');

    var xhr = new XMLHttpRequest();
    xhr.open('POST', form.action, true);
    xhr.onload = function(e) {
        // ...
    };
    xhr.send(formData);

    return false;
}

var form = document.querySelector('#registration');
sendForm(form);

FormData對象也能用來模擬File控件,進行文件上傳。

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);

FormData也可以加入JavaScript生成的文件。

// 添加JavaScript生成的文件
var content = '<a id="a"><b id="b">hey!</b></a>';
var blob = new Blob([content], { type: "text/xml"});
formData.append("webmasterfile", blob);

setRequestHeader()

setRequestHeader方法用于設置HTTP頭信息。該方法必須在open()之后、send()之前調用。如果該方法多次調用,設定同一個字段,則每一次調用的值會被合并成一個單一的值發送。

xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));

上面代碼首先設置頭信息Content-Type,表示發送JSON格式的數據;然后設置Content-Length,表示數據長度;最后發送JSON數據。

overrideMimeType()

該方法用來指定服務器返回數據的MIME類型。該方法必須在send()之前調用。

傳統上,如果希望從服務器取回二進制數據,就要使用這個方法,人為將數據類型偽裝成文本數據。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);

// 強制將MIME改為文本類型
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);
      var byte = c & 0xff;  // 去除高位字節,留下低位字節
    }
  }
};

xhr.send();

上面代碼中,因為傳回來的是二進制數據,首先用xhr.overrideMimeType方法強制改變它的MIME類型,偽裝成文本數據。字符集必需指定為“x-user-defined”,如果是其他字符集,瀏覽器內部會強制轉碼,將其保存成UTF-16的形式。字符集“x-user-defined”其實也會發生轉碼,瀏覽器會在每個字節前面再加上一個字節(0xF700-0xF7ff),因此后面要對每個字符進行一次與運算(&),將高位的8個位去除,只留下低位的8個位,由此逐一讀出原文件二進制數據的每個字節。

這種方法很麻煩,在XMLHttpRequest版本升級以后,一般采用指定responseType的方法。

var xhr = new XMLHttpRequest();
xhr.onload = function(e) {
  var arraybuffer = xhr.response;
  // ...
}
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.send();

XMLHttpRequest實例的事件

readyStateChange事件

readyState屬性的值發生改變,就會觸發readyStateChange事件。

我們可以通過onReadyStateChange屬性,指定這個事件的回調函數,對不同狀態進行不同處理。尤其是當狀態變為4的時候,表示通信成功,這時回調函數就可以處理服務器傳送回來的數據。

progress事件

上傳文件時,XMLHTTPRequest對象的upload屬性有一個progress,會不斷返回上傳的進度。

假定網頁上有一個progress元素。

<progress min="0" max="100" value="0">0% complete</progress>

文件上傳時,對upload屬性指定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);
}

upload(new Blob(['hello world'], {type: 'text/plain'}));

load事件、error事件、abort事件

load事件表示服務器傳來的數據接收完畢,error事件表示請求出錯,abort事件表示請求被中斷。

var xhr = new XMLHttpRequest();

xhr.addEventListener("progress", updateProgress);
xhr.addEventListener("load", transferComplete);
xhr.addEventListener("error", transferFailed);
xhr.addEventListener("abort", transferCanceled);

xhr.open();

function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = oEvent.loaded / oEvent.total;
    // ...
  } else {
    // 回應的總數據量未知,導致無法計算百分比
  }
}

function transferComplete(evt) {
  console.log("The transfer is complete.");
}

function transferFailed(evt) {
  console.log("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  console.log("The transfer has been canceled by the user.");
}

loadend事件

abortloaderror這三個事件,會伴隨一個loadend事件,表示請求結束,但不知道其是否成功。

req.addEventListener("loadend", loadEnd);

function loadEnd(e) {
  alert("請求結束(不知道是否成功)");
}

文件上傳

HTML網頁的<form>元素能夠以四種格式,向服務器發送數據。

  • 使用POST方法,將enctype屬性設為application/x-www-form-urlencoded,這是默認方法。
<form action="register.php" method="post" onsubmit="AJAXSubmit(this); return false;">
</form>

  • 使用POST方法,將enctype屬性設為text/plain
<form action="register.php" method="post" enctype="text/plain" onsubmit="AJAXSubmit(this); return false;">
</form>

  • 使用POST方法,將enctype屬性設為multipart/form-data
<form action="register.php" method="post" enctype="multipart/form-data" onsubmit="AJAXSubmit(this); return false;">
</form>

  • 使用GET方法,enctype屬性將被忽略。
<form action="register.php" method="get" onsubmit="AJAXSubmit(this); return false;">
</form>

某個表單有兩個字段,分別是foobaz,其中foo字段的值等于barbaz字段的值一個分為兩行的字符串。上面四種方法,都可以將這個表單發送到服務器。

第一種方法是默認方法,POST發送,Encoding type為application/x-www-form-urlencoded。

Content-Type: application/x-www-form-urlencoded

foo=bar&baz=The+first+line.&#37;0D%0AThe+second+line.%0D%0A

第二種方法是POST發送,Encoding type為text/plain。

Content-Type: text/plain

foo=bar
baz=The first line.
The second line.

第三種方法是POST發送,Encoding type為multipart/form-data。

Content-Type: multipart/form-data; boundary=---------------------------314911788813839

-----------------------------314911788813839
Content-Disposition: form-data; name="foo"

bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"

The first line.
The second line.

-----------------------------314911788813839--

第四種方法是GET請求。

?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.

通常,我們使用file控件實現文件上傳。

<form id="file-form" action="handler.php" method="POST">
  <input type="file" id="file-select" name="photos[]" multiple/>
  <button type="submit" id="upload-button">上傳</button>
</form>

上面HTML代碼中,file控件的multiple屬性,指定可以一次選擇多個文件;如果沒有這個屬性,則一次只能選擇一個文件。

file對象的files屬性,返回一個FileList對象,包含了用戶選中的文件。

var fileSelect = document.getElementById('file-select');
var files = fileSelect.files;

然后,新建一個FormData對象的實例,用來模擬發送到服務器的表單數據,把選中的文件添加到這個對象上面。

var formData = new FormData();

for (var i = 0; i < files.length; i++) {
  var file = files[i];

  if (!file.type.match('image.*')) {
    continue;
  }

  formData.append('photos[]', file, file.name);
}

上面代碼中的FormData對象的append方法,除了可以添加文件,還可以添加二進制對象(Blob)或者字符串。

// FilesformData.append(name, file, filename);// BlobsformData.append(name, blob, filename);// StringsformData.append(name, value);    

append方法的第一個參數是表單的控件名,第二個參數是實際的值,第三個參數是可選的,通常是文件名。

最后,使用Ajax方法向服務器上傳文件。

var xhr = new XMLHttpRequest();xhr.open('POST', 'handler.php', true);xhr.onload = function () {  if (xhr.status !== 200) {    alert('An error occurred!');  }};xhr.send(formData);

目前,各大瀏覽器(包括IE 10)都支持Ajax上傳文件。

除了使用FormData接口上傳,也可以直接使用File API上傳。


var file = document.getElementById('test-input').files[0];
var xhr = new XMLHttpRequest();

xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);

可以看到,上面這種寫法比FormData的寫法,要簡單很多。

參考鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容