一、初识multipart/form-data
enctype属性:
enctype:规定了form表单在发送到服务器时候编码方式,它有如下的三个值。
application/x-www-form-urlencoded
:默认的编码方式。但是在用文本的传输和MP3等大型文件的时候,使用这种编码就显得 效率低下。multipart/form-data
:指定传输数据为二进制类型,比如图片、mp3、文件。text/plain
:纯文体的传输。空格转换为 “+” 加号,但不对特殊字符编码。
二、multipart/form-data请求请求体的格式
multipart/form-data
是基于post方法来传递数据的,并且其请求内容格式为Content-Type: multipart/form-data
,用来指定请求内容的数据编码格式。另外,该格式会生成一个boundary
字符串来分割请求头与请求体的,具体的是以一个boundary=${boundary}
来进行分割,伪码如下:
...
Content-Type: multipart/form-data; boundary=${boundary}
--${boundary}
...
...
--${boundary}--
上面boundary=${boundary}
之后就是请求体内容了,请求体内容各字段之间以--${boundary}
来进行分割,以--${boundary}--
来结束请求体内容。
例子:
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"
title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
数据包说明
首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary
开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary--
标示结束。关于 multipart/form-data 的详细定义,可在 rfc1867 查看。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
具体传递过程
由于是POST请求,参数一般是以key-value的json字符串传递,或者将参数放入map传递。我们可以看到很多name,这个name就是key,下面的数据就是value,我们可以提取所有的key及value,组成一个json字符串或者map传递即可。
提取出的json字符串类似于下面字段所示(非以上数据包提取,源自:如何传递Request PayLoad(请求负载)中的数据):
{"view:id1:txtSearch":"","$$viewid":"!f9fpyhcv2t!","$$xspsubmitid":"view:_id1:_id2:pager1_Group_lnk_2","$$xspexecid":"view:_id1:_id2:parent","$$xspsubmitvalue":"","$$xspsubmitscroll":"0|0","view:_id1":"view:_id1"}
或者map形式:
Map<String,String> m = new HashMap<String,String>();
m.put("view:id1:txtSearch", "");
m.put("$$viewid", "!f9fpyhcv2t!");
m.put("$$xspsubmitid", "view:_id1:_id2:pager1_Group_lnk_2");
m.put("$$xspexecid", "view:_id1:_id2:parent");
m.put("$$xspsubmitvalue", "");
m.put("$$xspsubmitscroll", "0|0");
m.put("view:_id1", "view:_id1");
三、文中相关名词:
- boundary属性
根据RFC 1867定义,我们需要选择一段数据作为“分割边界”( boundary属性),这个“边界数据”不能在内容其他地方出现,一般来说使用一段从概率上说“几乎不可能”的数据即可。 不同浏览器的实现不同,每次post浏览器都会生成一个随机的30-40位长度的随机字符串,浏览器一般不会遍历这次post的所有数据找到一个不可能出现在数据中的字符串,这样代价过大,一般都是随机生成。
Rfc1867这样说明{A boundary is selected that does not occur in any of the data. (This selection is sometimes done probabilisticly.)}。
(待更新)
参考:
- 四种常见的 POST 提交数据方式:https://imququ.com/post/four-ways-to-post-data-in-http.html
- 谈谈form-data请求格式:https://www.cnblogs.com/wonyun/p/7966967.html
- Multipart/form-data POST文件上传详解:https://blog.csdn.net/xiaojianpitt/article/details/6856536
- POST之multipart/form-data请求:https://www.jianshu.com/p/0023bb7afddb