使用 JQuery 透過 FormData 上傳檔案 (headers 帶 boundary)

基礎知識

JQuery AJAX: contentType

  • 不填寫時,預設為 application/x-www-form-urlencoded

FormData

  • FormData 介面可為表單資料中的欄位/值建立相對應的的鍵/值對(key/value)集合,之後便可使用 XMLHttpRequest.send() 方法來送出資料。

  • 它在編碼類型設定為 multipart/form-data 時會採用與表單相同的格式送出。

RFC1867

剛開始,http 協議中没有檔案上傳的功能,直到 RFC1867 為 http 協議添加了此功能。

RFC1867 中限定:
  • <form>method 必須是 POST

  • enctype 屬性新增了 multipart/form-data 的選項。

  • <input>type 屬性增加了 file 選項。


程式碼

html

1
2
3
4
5
6
7
<form action="server/api/items" name="productForm" enctype="multipart/form-data" method="POST">
<h3>新增商品</h3>
<input type="file" name="files" class="productFile">
<input type="text" class="productName">
<input type="number" min="1" class="productPrice">
<input type="submit" value="送出">
</form>

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 取得表單
let productForm = document.forms.namedItem('productForm')
productForm.addEventListener('submit', function(event){
post_items(event, productForm)
})


function api_post_items (event, form){

// 取消表單預設提交
event.preventDefault()

let name = document.querySelector('.productName').value
let stock = document.querySelector('.productAmount').value
let unit_price = document.querySelector('.productPrice').value
let file = $('.productFile')[0].files[0] // 單個檔案

// 建立一個新的 FormData 物件
let formData = new FormData(form)

// 追加新值到 FormData 物件已有的對應鍵上;若該鍵不存在,則為其追加新的鍵
formData.append('name', name)
formData.append('cost', cost)
formData.append('unit_price', unit_price)
formData.append('images', file)

let item = {
'url': `${ server }/api/items`,
'type': 'POST',
'headers': {
// 'Content-Type': 'multipart/form-data',
// 使用 multipart/form-data 在此不需要設定 Content-Type。
'X-Requested-With': 'XMLHttpRequest',
'Authorization': `Bearer ${ userToken }`,
},
'contentType': false, //required
'processData': false, // required
'mimeType': 'multipart/form-data',
'data': formData
}

$.ajax(item)

.done(function (response) {
console.log(response)
closeLightBox()
api_get_items()
})

.fail(function (response) {
console.log('api_post_user: Fail ' + response.responseText)
})
}

注意!

  • <form> 表單設定 enctype="multipart/form-data"

  • <form> 中,需要有 <input type="file">

  • JavaScript 中 設定 contentType = false

    • 在傳送 multipart/formdata 時,希望不影響原表單設定,而直接將預設值改為 multipart/form-data
  • JavaScript 中 設定 processData = false

    • 不去處理資料。
  • JavaScript 中 設定 cache = false,檔案不需緩存。

表單送出後,去檢查 Request Headers 看到我們有帶 boundary 成功傳輸。


Reference

FormData - MDN
Content-Type - MDN
通过jQuery Ajax使用FormData对象上传文件
浅谈contentType = false
从FormData到图片上传
Submitting multipart/form-data using jQuery and Ajax
missing boundary in Content-Type for multipart posts

自訂一個指令同時更新及備份 hexo 部落格(macOS) ngrok 的設定,來架一個臨時伺服器!

留言