序言
?最近在寫項目的過程中有一個需求是利用 HTTP 協議傳輸圖片和視頻,經過查詢方法相應的方法發現使用 multipart/form-data
的方式,這是最常見處理二進制文件的表單編碼類型。
?學習了一下午,現在總結一下使用的方法和相關的知識點,歡迎指正😄!
一、如何使用?
??
注:我們的開發語言是 C++,主要使用到的庫是 httplib,幫助我們快速的搭建HTTP 服務器,以及相應數據的處理
1. 前端邏輯
?首先我們配合使用 httplib
快速的搭建一個網絡首頁:
這里界面有些簡陋哈哈,但是學習的意義大于外表!
這里需要用戶填寫昵稱和一個圖片(分別對應純文本數據和圖片數據稍后形成對比)。前端的代碼非常的簡潔,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"><title>Simple Form</title>
</head>
<body><form action="/submit" method="POST" enctype="multipart/form-data"><label>昵稱:</label><input type="text" name="nickname" required><br><label>圖片:</label><input type="file" name="image" accept="image/*" required><br><button type="submit">Submit</button></form>
</body>
</html>
我們來逐個介紹每一個關鍵元素的作用,首先就是 form
元素,用于定義用戶可以填寫并提交數據的表單。它負責收集用戶輸入并將數據發送到指定的服務器地址:
action
:定義表單提交的目標地址,比如這里就表示提交到當前域名下的/submit
路徑(對于處理表單數據的路徑)method
:提交表單時最常用的就是POST
方式enctype
:定義表單數據的編碼類型,影響服務器解析數據的方式。常見值:application/x-www-form-urlencoded(默認)
: 適合普通鍵值對數據(比如賬號和密碼)multipart/form-data
: 必須在表單 包含文件上傳 時使用text/plain
: 將數據以 純文本形式 發送,較少使用
現在我們在介紹 input
元素,來控制用戶的輸入:
type
:我們這里使用了text
代表普通文本輸入;以及file
代表傳輸特定的文件(支持accept
屬性限制文件類型)name
:定義該輸入框的名稱,用于在表單提交時標識字段,在后面可以根據特定的字段提取相應的數據required
:代表數據必須填寫,不可為空
這就是前端的邏輯,很多時候前端占據多數的都是樣式的調整,咋們直接抽絲剝繭來一個最基礎版的幫助大家理解。
2. 后端邏輯
?通過前端的邏輯我們得知,用戶會提交一個昵稱和一個圖片以 POST
的方式發送到 /submit
下進行處理。那我們怎么實現呢,我們使用一個 httplib
提供的接口,如下:
Server &Post(const std::string &pattern, Handler handler);
Post
: 代表處理Post
方式傳輸的數據pattern
:表示相應的處理路徑,這里我們就應該傳入/submit
handler
:定義為using handler = std::function<void(const httplib& Requests, httplib::Response&)>
我們定義的處理函數,需要符合他的參數返回值
但是在使用之前,我們還需要學習一個知識點,那就是如何將表單里面的數據以取出來!
?大家記好了,我們表單當中的每一個數據都以 MultipartFormData
的形式存儲在 httplib& Requests
中,我們只需要每個數據的 命名 即可提取(咦?命名 怎么來的呀?如果你感到疑惑,請移步到 input
元素的 name
字段)。具體使用方式:
/*部分代碼*/
void HandlePost(const httplib::Request &req, httplib::Response &rsp)
{/*這里是做嚴謹地檢查,是否存在該命名的數據*/if (req.has_file("nickname")) {/*提取表單中單個元素的數據*/httplib::MultipartFormData nickname = req.get_file_value("nickname");
其實這個類型就是一個結構體,存儲數據的相關信息:
struct MultipartFormData {std::string name; /*數據的名稱*/std::string content; /*數據的內容*/std::string filename;/*文件的名稱(如果他是一個文件,否則空)*/std::string content_type;/*數據類型*/
};
咋們打印看一下上傳的圖片的文件信息,就不打印內容了是亂碼:
image/jpeg
AVL.jpg
image
現在基本的使用咋們理解了,那底層是咋一回事呢?
二、 HTTP 報文傳輸格式
?提供使用瀏覽器的網絡抓包,我們先來看一下傳輸的 請求頭 是怎么樣的:
發現在數據類型后面多了一個字段 boundary(分界線)
,boundary
是客戶端(如瀏覽器)自動生成的,用來分隔 HTTP
請求體中的多個部分(因為表單當中有多個類型的數據需要間隔開)。
?現在我們看一下正文內容的存儲格式:
兩個數據一個是文本一個是圖片(但是圖片的數據沒有正常顯示)。
總結
?HTML表單 是網頁中與用戶交互的重要元素,允許用戶輸入數據并將其發送到服務器進行處理。也許我們可以自己嘗試設計一下解析 multipart/form-data
報文請求的方法。紙上得來終覺淺,絕知此事要躬行!