"submit" name="submit" value="提交"/>

请求头信息如下
post /article/handle.php http/1.1
host: www.soapstudy.com
user-agent: mozilla/5.0 (windows nt 6.1; rv:41.0) gecko/20100101 firefox/41.0
accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8
accept-language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
accept-encoding: gzip, deflate
referer: http://www.soapstudy.com/article/postupload.html
connection: keep-alive
content-length : 65
content-type: application/x-www-form-urlencoded
消息体:
uname=1234&email=12345&file=desktop.jpg&submit=提交
看着眼熟,类似于 get方式传输地址栏中的 参数名1=参数值1&参数名2=参数值2 的形式。很明显,此种方式只是将文件名称通过post方式传输给服务器,仅有名称而无文件信息,这样是无法上传文件的。
接下来看第二类代码
"handle.php" method="post" enctype="multipart/form-data">
"text" name="uname" class="uname" />
"text" name="email" class="email" />
"file" name="file" class="file" />
"submit" name="submit" value="提交"/>
和第一段代码唯一不同的地方是form表单多了属性 enctype=”multipart/form-data” 接着在表单中输入同样的内容提交,请求信息如下
请求头信息
post /article/handle.php http/1.1
host: www.soapstudy.com
user-agent: mozilla/5.0 (windows nt 6.1; rv:41.0) gecko/20100101 firefox/41.0
accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8
accept-language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
accept-encoding: gzip, deflate
referer: http://www.soapstudy.com/article/postupload.html
connection: keep-alive
content-length:529278
content-type:multipart/form-data;boundary=---------------------------11810260022182
(请求头信息和第一种方式不同的地方是 content-type, 第一种方式为application/x-www-form-urlencoded, 而此种方式为multipart/form-data; 紧跟着就是 boundary=---------------------------11810260022182——这是用来分割消息体中不同内容的,可以认为是边界,在消息体中可以看到此项应用)
消息体
(图一)

中间那乱七八糟的一大堆的东西应该就是我们的文件信息,这样服务器端收到这些信息以后,就可以将这些信息处理成相应的文件了。
通过以上案例的两种方式可以得出form表单提交数据的两种方式
1. application/x-www-form-urlencoded
2. multipart/form-data
第一种方式不能用于上传文件,只能提交文本,当然如果有file控件的话也只能提交文件名,服务器端(以php为例)可以通过 $_post[]的方式接收。(可以参考 关于application/x-www-form-urlencoded 这篇文章)
第二种方式——也是我们接下来将要讨论的方式——可用于上传文件,当然服务器端处理方式不同,通过$_files 来接收文件信息
下面我们着重讨论multipart/form-data
(为了下文书写方便,我们将boundary=---------------------------11810260022182定义成一个变量$boundary 此变量的值为---------------------------11810260022182)
正如它的名字(form-data)所描述的一样,multipart/form-data 是用来表示表单提交的数据的,它的格式和所有的multipart mime 数据流的格式一样。如rfc 2388描述的那样,multipart/form-data 包含很多部分,各部分之间用 --$boundary分割 其格式如下
--$boundary //开始
content-disposition: form-data;name=”field1”
content-type:text/plain
content-transfer-encoding:******
内容
--$boundary
content-disposition: form-data;name=”field2”
content-type:image/jpeg
内容
--$boundary-- //结束
接下来对每一项做一个大概的解释
1、--$boundary
边界是由 “--”加上$boundary(在请求头的content-type中定义的boundary=---------------------------11810260022182,见上述案例的第二种实现方式),因此消息体中的边界是比请求头中定义的多两个‘-’。对于$boundary的这个值来说,不同的应用程序(当然我们这里说的就是浏览器)产生的规则不同。
google浏览器:

火狐浏览器:

但是有一点可以肯定,这个值不会和数据部分抑或是数据部分的某一段相同,当然相同的几率也是有的,不过概率非常之小,如果被我们碰上,那我们也算是相当的幸运了。
2、content-disposition
此项用于指定当前的呈现方式。 multipart/form-data 包含有很多部分,每一部分都包含一个content-disposition 头(定义在rfc 2183 )。此项的类型 为 form-data。对于name项,其值为form表单中 input的属性name的值。当然,除了类型和name这两项还可能有其它的属性,例如在案例中第二种方式的图一中可以看到第三部分还有filename项。根据传输的数据的类型的不同,disposition 下面的内容是不同的。详细了解可以参考rfc 2183。
3、content-type
传输内容的类型 在multipart/form-data包含的每一项中都会有content-type选项,此项的默认类型为 text/plain(文本形式)。像图一中的前两部分内容是文本,因此content-type的类型为text/plain 由于此项为默认类型,所以有些应用程序是不显示出来的,而对于图一中的第三部分是一个图片的时候,content-type的类型为image/jepg。
当然此项是根据表单中的input框中的内容指定的,如果一个文件可以被作为一个正确的媒体类型,那content-type 的值就为此文件的类型,例如application/octet-stream、image/jpeg等。
4、content-transfer-encoding
当传输内容的编码方式不符合默认的编码方式的时候,此项会被用来指定相应的编码方式——可见图一中各部分数据的编码方式都符合,因此此项没有显示。
5、数据
此项就是编码以后的数据了
通过对以上几项的解释,对multipart/form-data 的格式有了一个大致的了解。当然,了解这些可能对我们当下编写只使用form表单上传文件的程序来说没有什么帮助,但是在我看来,多了解一些细节对于我们以后编写类似的程序会有很大的帮助,毕竟对于我们程序员来说要知其然,更要知其所以然。
由于本人水平有限,无法触及到更深层次的知识,本篇权当抛砖引玉,希望有高手指教,如有兴趣请在下方留言,共同讨论,共同进步