一.什么是multipart/form-data
首先我們需要明白在html中的enctype屬性.
enctype:規定了form表單在發送到服務器時候編碼方式。有如下的三個值。
①application/x-www-form-urlencoded。默認的編碼方式。但是在用文本的傳輸和MP3等大型文件的時候,使用這種編碼就顯得 效率低下。
②multipart/form-data 。 指定傳輸數據為二進制類型,比如圖片、mp3、文件。
③text/plain。純文體的傳輸。空格轉換為 “+” 加號,但不對特殊字符編碼。
二.明確在enctype參數為application/x-www-form-urlencoded的時候post和get請求參數和請求體是什么形式的
2.1 get請求
請求頭:
GET/www.xxx.com?name=%22hello+world%22&**file=temp.png**&submit=submit HTTP/1.1
因為get請求沒有請求體,所有他的所有參數都是在url的后邊添加。type=file的表單項只能獲取到文件的名字,並不能獲取文件的內容。
2.2 post請求
請求頭:
POST /www.baidu.com HTTP/1.1
請求體:
name=%22hello+world%22&file=temp.png&submit=submit
(1)我們可以發現不管是post請求和get請求,他們的參數存在的形式都是不變的,通過key=value的形式存在。
(2)表單項type=filed只能獲取獲取文件的名字不能獲取文件的內容。
三. multipart/form-data
...
Content-Type: multipart/form-data; boundary=${boundary}
--${boundary}
...
...
--${boundary}--
請求內容格式為Content-Type: multipart/form-data,用來指定請求內容的數據編碼格式。
3.1 boundary字符串
該格式會生成一個boundary字符串來分割請求頭與請求體的,具體的是以一個boundary=${boundary}來進行分割。
1、上面boundary=${boundary}之后就是請求體內容了,
2、請求體內容各字段之間以--${boundary}來進行分割,
3、以--${boundary}--來結束請求體內容。
3.2 get請求 無效
請求頭:
GET/www.xxx.com?name=%22hello+world%22&file=temp.png&submit=submit HTTP/1.1
get請求和multipart/form-data結合無效,因為文件上傳需要請求體。
3.3 post請求:
請求頭:
POST /www.xxx.com HTTP/1.1
請求體:
------WebKitFormBoundaryIZDrYHwuf2VJdpHw
Content-Disposition: form-data; name="name"
"hello world"
------WebKitFormBoundaryIZDrYHwuf2VJdpHw
Content-Disposition: form-data; name="file"; filename="temp.png"
Content-Type: image/png
.PNG
.
...
IHDR...
..........Y../..,+|.$aIk.v...G?...P.P,,...m..e.2....v.7. pHYs...%...%.IR$....|IDAT(.cTT....................:.?.......}.(.Pd`A..V...L...?..#.....4.o..LS.....W.d.?...A8..LS...(.u.......D.b......b.....o&..;..<.1......IEND.B`.
------WebKitFormBoundaryIZDrYHwuf2VJdpHw
Content-Disposition: form-data; name="submit"
submit
------WebKitFormBoundaryIZDrYHwuf2VJdpHw--
3.4 多部件請求體
通過觀察發現這個的請求體就發生了變化。這種請求體被稱之為多部件請求體。
什么是多部件請求體:就是把每一個表單項分割為一個部件。
因為表單項分為普通表單項和文件表單項,所以說部件也有區別。
3.4.1 普通表單項:
- 一個請求頭:Content-Disposition: form-data; name=”name”
- 一個請求體:里面就是我們表單的值”hello world”
3.4.2 文件表單項:
- 兩個請求頭:
- Content-Disposition: form-data; name="file";
- filename="temp.png"
- 一個請求體:
.PNG
.
...
IHDR...
..........Y../..,+|.$aIk.v...G?...P.P,,...m..e.2....v.7. pHYs...%...%.IR$....|IDAT(.cTT....................:.?.......}.(.Pd`A..V...L...?..#.....4.o..LS.....W.d.?...A8..LS...(.u.......D.b......b.....o&..;..<.1......IEND.B`.
------WebKitFormBoundaryIZDrYHwuf2VJdpHw
Content-Disposition: form-data; name="submit"
submit
------WebKitFormBoundaryIZDrYHwuf2VJdpHw--
3.5 FormData對象
XMLHttpRequest Level 2添加了一個新的接口FormData。利用FormData對象,我們可以通過JavaScript用一些鍵值對來模擬一系列表單控件,我們還可以使用XMLHttpRequest的send()方法來異步的提交這個"表單"。
var formData = new FormData();
formData.append("username", "Groucho");
formData.append("accountnum", 123456);
fetch('/users', {
method: 'POST',
body: formData
})
上面創建了一個FormData對象,通過fetch進行ajax請求時,會自動為其將其轉為form-data格式,無需手動添加格式。
3.6 對象轉FormData對象
對於FormDat對象,像上面那種形式可以直接添加參數比較方便,但是對於對象或者嵌套對象:
let userObj = {userName: ’xxx', age: '21'};
formData.append('user', userObj);
上面形式添加formData參數user,並不會獲取到其真正的內容,而是返回userObj的Object.prototype.toString.call(userObj)的值作為user字段的值。
------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="user"
[object Object]
遺憾的是,FormData對象沒有像JSON.stringify那樣的方法能批量將對象形式轉換為對應的形式,formData而言是將對象的key轉換為正確formData請求參數字段名,例如如下對象:
var obj = {
a: '2',
b: {c: 'test'},
c: [
{id: 1, name: 'xx'},
{id:2 ,name: 'yy', info: {d: 4} }
]
}
這樣轉換為FormData對象時,其對應的key應該是下面這樣的:
a: 2
b[c]: test
c[][id]: 1
c[][name]: xx
c[][id]: 2
c[][name]: yy
c[][info][d]:4
這樣,就需要我們自己手動來實現一個轉換數據函數,具體代碼如下:
function objectToFormData (obj, form, namespace) {
const fd = form || new FormData();
let formKey;
for(var property in obj) {
if(obj.hasOwnProperty(property)) {
let key = Array.isArray(obj) ? '[]' : `[${property}]`;
if(namespace) {
formKey = namespace + key;
} else {
formKey = property;
}
// if the property is an object, but not a File, use recursivity.
if(typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
objectToFormData(obj[property], fd, formKey);
} else {
// if it's a string or a File object
fd.append(formKey, obj[property]);
}
}
}
return fd;
}
這樣,就可以將對象轉化為對應的formData的格式了。
