form 標籤與 FormData 的應用
# 前端上一篇文章中,我們介紹了 multipart/form-data 的請求格式以及它想解決的問題。這篇文章中會以實際開發上遇到的問題跟應用做解說。
具體來說,這篇文章會包含下列幾個有關於表單應用的部分:
<form/>標籤背後做了哪些事- FormData 在 JavaScript 當中的應用
- FormData 與
fetch的搭配 - JavaScript 如何操作檔案上傳
再談 form 標籤
HTML 的 form 標籤裡頭其實有許多瀏覽器幫你實作的細節,開發者有時候會忽略。以下面的例子來說:
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="text" name="name" />
<input type="file" name="file" />
<button>
Submit
</button>
</form>
在沒有任何 JavaScript 程式碼的情況下,按下 Submit 按鈕之後瀏覽器會幫你做這些事情:
- 序列化 input 當中的 name 與 file 欄位
- 以 POST 方法送出
Content-Type: multipart/form-data的 HTTP 請求 - 讀取檔案並加入到請求內容中(如果檔案存在)
在單頁應用、前端框架還不流行的時候,用 form 表單填寫資料送出,然後重新導向到其他頁面是常見的做法。但是當填寫的資料變多,或是只有部分區域需要更新(例如留言等)時每次都要整頁更新對使用者來說體驗並不好,所以逐漸衍生出透過 ajax 打 API,並用 JavaScript 動態更新資料的做法。

雖然動態更新的方式的確改善了使用者體驗,但是要實現一個好的表單設計卻需要考慮許多細節:
- 錯誤處理
- 狀態轉換
- 資料保存
- Accessibility
很多時候只要一個環節沒有做好,使用者反而還寧願用單純整頁更新的表單標籤來操作。對於像是後台應用來說,用 <form> 表單來做整頁更新往往可以省下很多開發時間,甚至依賴瀏覽器的內建機制運作起來更加穩定。
FormData 在 JavaScript 中的應用
FormData 定義了一個介面方便開發者做像是 key/value 的應用,最常見的就是表單處理。一個 FormData 的宣告可以這樣子寫:
const formData = new FormData()
// key value
formData.append('name', 'Kalan');
如果將 form 的 element 放到 FormData 當中,會自動將裡頭填寫的資訊直接序列化成 FormData:
<form id="form" enctype="multipart/form-data" action="/upload" method="POST">
<input type="text" name="name" />
<input type="file" name="file" />
<button>Submit</button>
</form>
<script>
const formData = new FormData(document.getElementById('form'));
formData.get('name'); // 取得目前 input 的值
formData.get('file'); // 取得目前的檔案
</script>

除此之外,如果將 FormData 放到 fetch 的 body 裡頭,瀏覽器會自動幫你以 multipart/form-data 的形式傳送:
const formData = new FormData();
formData.append('name', 'Kalan');
formData.append('file', new File(['Hello World'], 'file.txt', { type: 'text/plain' }))
fetch('/upload', {
method: 'POST',
body: formData
})
在執行完上面的 JavaScript 程式碼並觀察 Network 的請求,可以發現儘管沒有特別定義 Content-Type,瀏覽器還是會幫我們以 multipart/form-data 的方式傳送,form data 的序列化也是由瀏覽器完成

總結
這兩篇文章解釋了 multipart/form-data 的應用與實際使用方法。第一篇文章解釋了 Content-Disposition 的含義, boundary 的用途,以及 multipart/form-data 的請求如何構造;第二篇文章說明了在實務上我們可以怎麼使用 form 與 FormData,並透過 JavaScript 來做 FormData 的處理與檔案上傳。
對於伺服器端來說,在網頁上做檔案上傳的動作也是一個 HTTP 請求,所以伺服器端必須要根據 Header 當中的資訊以及 multipart/form-data 定義的格式來解析資料,才有辦法正確拿到檔案內容,通常這些解析都已經被框架處理掉,但是在這邊要特別註明的是,在網頁上傳遞檔案沒有太神奇的魔法,背後仍然是奠基在 HTTP 請求之上。
相關文章
- 讓你的超連結底線更好看:text-underline-offset預設的情況下底線跟文字很接近,有些設計師不喜歡這種樣式,我自己也覺得沒有很好看。
- 為什麼網頁不應該追求 Pixel Perfect只有在 Pixel Perfect 很重要的時候才應該關注他,否則往往會讓你得到一個雙輸的局面。
- 用 CSS HSL 來寫顏色吧!(以及更好的方法)在網頁開發中,傳統的 HEX 和 RGB 顏色表示法雖然廣泛使用,但存在不易閱讀和直觀性不足的問題,且在廣域的色彩空間如 P3 下表現能力受限。HSL(色相、飽和度、亮度)提供了一種更直覺的顏色定義方式,讓開發者能更輕鬆地理解與調整顏色。HSL 通過色相、飽和度和亮度三個維度來描述顏色,使得顏色的調整更加人性化,特別是在設計系統中,HSL 能更好地呈現色盤的亮度變化。
- 盡可能不設定 line-height 為 1 與 ellipsis 的原因本文探討了為何在網頁設計中不建議將 line-height 設為 1,以及使用 ellipsis 時遇到的語言問題