開發 Vue 問題挑戰 ─ 在 Vue 中使用 Fetch 上傳檔案

之前都是使用 axios 套件來做 API 串接的,但是既然有原生的 Web API 可以來操作 AJAX ,就來試試看!

Fetch 基礎寫法

1
2
3
4
5
6
7
8
9
10
11
12
13
fetch(url, {
// init
method: 'GET' || 'POST' || 'PUT' || 'PATCH' || 'DELETE',
headers: {
'Content-Type': 'application/json', // HTTP 請求中,客戶端用來告訴伺服器自己傳的資料是什麼內容類型。
Accept: 'application/json', // HTTP 請求標頭對伺服器告知用戶端可解讀的內容類型。
},
body: JSON.stringify(data) // 如果要傳送資料( 'POST' || 'PUT' || 'PATCH' ), 則透過 body 傳送資料
})
// 透過 res.json() 來解讀回傳回來的資料
// res.json() 回傳回來的是一個 Promise ,所以要用 then 接收 resolve
.then(res => res.json())
.then(json => console.log(json))

問題:如何透過 fetch 傳送 照片檔案

因為之前都是用 axios 來串接 API ,所以之前要上傳檔案時,使用 axios 的寫法是這樣的 ⬇️。

1
2
3
4
5
6
7
8
const formData = new FormData;
formData.append('file', document.querySelector('input[type="file"]').files[0]);

axios.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
}
}).then(data => console.log(data))

所以在改寫成 fetch 時,順手的寫成這樣 ⬇️

1
2
3
4
5
6
7
8
9
10
11
12
const formData = new FormData;
formData.append('file', document.querySelector('input[type="file"]').files[0]);

fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
},
body: formData
})
.then(res => res.json())
.then(json => console.log(json))

但出現了錯誤:

因為錯誤提示看起來像是 CORS 的問題,所以都是朝這方面去找答案的,所以加入 mode:'no-cors' ,無法解決,所以想說會不會是我用 Vue CLI 構建工具導致不同源,所以又上網找答案,所以我又在 devServer 上加入一堆配置項,結果還是沒辦法😢!這時候的我已經要耗費一天處理這個紅色警告了💢 💢 💢!!

後面經過場外求助才發現原來是這個 'Content-Type': 'multipart/form-data' 導致問題的!

後來認真看了一下 MDN 的範例,才發現範例中也沒有加入 'Content-Type'

主要原因似乎是因為要上傳的資料(formData)的編碼類型就是設定為 'multipart/form-data' 了,所以如果在 fetch 中加入'Content-Type': 'multipart/form-data' ,就會因為重複指定而出錯。

所以在上傳檔案出錯時,可以先看看是不是加了 'Content-Type'

正確寫法 ⬇️

1
2
3
4
5
6
7
8
9
const formData = new FormData;
formData.append('file', document.querySelector('input[type="file"]').files[0]);

fetch(url, {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(json => console.log(json))

解答

How do I POST with multipart form data using fetch?

踩坑篇–使用 fetch 上传文件

Uploading files using ‘fetch’ and ‘FormData’

問題如下

See the Pen test by Celeste6666 (@celeste6666) on CodePen.