form表單提交
傳統(tǒng)的form表單提交只需要一個form標簽水醋,指定action、method='POST',并期望服務(wù)器返回一個text/html頁面幔虏。
可以看到請求頭中的字段产喉,指明了content-type捂掰、content-length,post的內(nèi)容為application/x-www-form-urlencoded格式的純文本曾沈,這個mime類型也是表單的默認編碼屬性(enctype)
再來看看表單post上傳文件的情況这嚣。最早的HTTP POST是不支持文件上傳的,給編程開發(fā)帶來很多問題塞俱。但是在1995年姐帚,ietf出臺了rfc1867,也就是《RFC 1867 -Form-based File Upload in HTML》,用以支持文件上傳障涯。所以Content-Type的類型擴充了multipart/form-data用以支持向服務(wù)器發(fā)送二進制數(shù)據(jù)罐旗。form需要被指定enctype=multipart/form-data
<form method="POST" enctype="multipart/form-data" action="xxx">
<input type="file" name="file">
</form>
from設(shè)置了multipart/form-data
后就將以二進制形式傳輸數(shù)據(jù)了。
可以看到請求頭中指定了mime類型與請求體數(shù)據(jù)分隔符像樊。根據(jù)RFC 1867定義尤莺,我們需要選擇一段數(shù)據(jù)作為“分割邊界”( boundary屬性),這個“邊界數(shù)據(jù)”不能在內(nèi)容其他地方出現(xiàn)生棍,一般來說使用一段從概率上說“幾乎不可能”的數(shù)據(jù)即可颤霎。
下面的數(shù)據(jù)便根據(jù)boundary劃分段,每一段便是一項數(shù)據(jù)涂滴。(每個field被分成小部分友酱,而且包含一個value是"form-data"的"Content-Disposition"的頭部;一個"name"屬性對應(yīng)field的ID,等等柔纵,文件的話包括一個filename)缔杉。
數(shù)據(jù)內(nèi)容以兩條橫線結(jié)尾,并以一個換行結(jié)束搁料。
傳統(tǒng)表單post提交發(fā)送兩種格式的數(shù)據(jù)或详,application/x-www-form-urlencoded -> 請求體為純文本; multipart/form-data -> 請求體為二進制數(shù)據(jù)
ajax post
ajax send支持以下幾種數(shù)據(jù)類型:
void send(ArrayBufferView data);
void send(Blob data);
void send(Document data);
void send(DOMString data);
void send(FormData data);
總體可分為郭计,發(fā)送 文本 / 二進制 數(shù)據(jù)霸琴。文本(Document, DOMString),二進制(Blob昭伸,ArrayBufferView梧乘,F(xiàn)ormData)
DOMString
xhr.send(DOMString)
類型時,若之前未指定請求頭的Content-Type
,則默認發(fā)送text/plain
類型的數(shù)據(jù)选调;而一般服務(wù)器期望接收urlencoded或json格式的數(shù)據(jù)夹供,以解析得到對象。因此在發(fā)送前需指定:
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // 或
xhr.setRequestHeader('Content-Type', 'application/json');
Doucment
Blob仁堪、ArrayBufferView
Blob是計算機界通用術(shù)語之一哮洽,全稱寫作:BLOB (binary large object),表示二進制大對象枝笨。
創(chuàng)建Blob對象的方法有幾種袁铐,可以調(diào)用Blob構(gòu)造函數(shù),詳情移步 https://developer.mozilla.org/en-US/docs/Web/API/Blob
xhr可以直接send一個Blob對象,請求頭中的Content-Type等于創(chuàng)建Blob時指定的MIME Type
var data = new Blob(['hello world'], {type: 'text/plain'});
xhr.send(data);
關(guān)于Blob横浑、ArrayBufferView剔桨、File三個類型之間的關(guān)系,可以用下圖簡單表述:
ArrayBuffer是一段內(nèi)存徙融,要操作它必須通過ArrayBufferView(是一個Helper類型洒缀,它的具體實現(xiàn)有TypedArray與DataView兩種);
構(gòu)造Blob接收
ArrayBuffer
欺冀、ArrayBufferView
树绩、DOMString
、Blob
四種類型的數(shù)據(jù)隐轩,以及MIMIE type參數(shù)饺饭。Blob對象有slice方法可以切割數(shù)據(jù),可以append一段ArrayBuffer數(shù)據(jù)职车;此外這個類型的最常見用途是在xhr.send()
與URL.createObjectURL()
瘫俊。ArrayBuffer表示二進制數(shù)據(jù)的原始緩沖區(qū),該緩沖區(qū)用于存儲各種類型化數(shù)組的數(shù)據(jù)悴灵,是二進制數(shù)據(jù)通用的固定長度容器扛芽。
類型化數(shù)組(Typed Arrays)是JavaScript中新出現(xiàn)的一個概念,專為訪問原始的二進制數(shù)據(jù)而生积瞒。
DataView對象在可以在ArrayBuffer中的任何位置讀取和寫入不同類型的二進制數(shù)據(jù)川尖。
來看兩個例子:
1、直接操作ArrayBuffer 并send ArrayBufferView 來模擬發(fā)送表單文本數(shù)據(jù)
var xhr = new XMLHttpRequest();
xhr.open('POST', '/onpost');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var ab = new ArrayBuffer(10);
var tab = new Int8Array(ab);
for (var i=0; i<tab.length; i++) {
tab[i] = 97+i;
}
tab[5]=61; // =
xhr.send(tab);
來看看請求報文:
POST http://localhost:10000/onpost HTTP/1.1
Host: localhost:10000
Connection: keep-alive
Content-Length: 10
Origin: http://localhost:10000
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Referer: http://localhost:10000/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
abcde=ghij
2茫孔、發(fā)送Blob數(shù)據(jù)
var bb = new Blob(['hello=world'], {type:'application/x-www-form-urlencoded'});
xhr.send(bb);
效果與例子1相同叮喳。
當然,實際應(yīng)用中不會用這種方式來發(fā)text缰贝。嘲更。這里僅是模擬,表明服務(wù)器解析報文是根據(jù)Content-Type與請求體內(nèi)容來的揩瞪。有傳輸其他MIME Type的二進制數(shù)據(jù)的需求,服務(wù)器端需要作相應(yīng)的處理篓冲。NodeJS的常用parser中間件body-parser李破、multer宠哄,分別只能解析application/x-www-form-urlencoded與application/json
,multipart/form-data
類型嗤攻。
http://www.reibang.com/p/83be90945336 這篇文章對于js操作毛嫉、傳輸二進制數(shù)據(jù)的講解十分通俗易懂。
File
File顧名思意就是“文件”妇菱,通常而言承粤,表示我們使用file控件(<input type="file">)選擇的FileList對象,或者是使用拖拽操作搞出的DataTransfer對象闯团。
這里的File對象也是二進制對象辛臊,因此,從屬于Blob對象房交,Blob對象的一些屬性與方法彻舰,F(xiàn)ile對象同樣適合,且推薦使用Blob對象的屬性與方法候味。
來看一個xhr.send(file)
的例子:
var file = $('input[type="file"]').files[0]; // 我上傳了一個jpg
xhr.send(file);
請求頭:
POST http://localhost:10000/onpost HTTP/1.1
Host: localhost:10000
Connection: keep-alive
Content-Length: 178854
Origin: http://localhost:10000
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36
Content-Type: image/jpeg
Accept: */*
Referer: http://localhost:10000/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
瀏覽器會根據(jù)file的MIMIE類型填寫Content-Type刃唤,以及Content-Length(字節(jié)數(shù))。
FormData
以上傳輸二進制數(shù)據(jù) / 文件的方法都不如FormData常用白群。
XMLHttpRequest Level 2添加了一個新的接口FormData. 利用FormData對象尚胞,我們可以通過JavaScript用一些鍵值對來模擬一系列表單控件,我們還可以使用XMLHttpRequest的send()方法來異步的提交這個”表單”帜慢。比起普通的ajax, 使用FormData的最大優(yōu)點就是我們可以異步上傳一個二進制文件笼裳。
xhr.send(FormData)
時,與傳統(tǒng)表單POST multipart/from-data一樣崖堤,指定了請求頭中的Content-Type侍咱、Boundary。