post Upload上传文件中multipart/form-data 做的那些事
大家在使用form上传文件的时候都用到过 enctype=”multipart/form-data” 这个属性,那multipart/form-data 到底有什么作用呢,下面我们就来聊一聊这个话题。首先我们先看一个案例
看第一种代码
<form action="handle.php" method="post" >
<input type="text" name="uname" class="uname" /><br />
<input type="text" name="email" class="email" /><br />
<input type="file" name="file" class="file" /><br />
<input type="submit" name="submit" value="提交"/>
</form>
请求头信息如下
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=%E6%8F%90%E4%BA%A4
看着眼熟,类似于 get方式传输地址栏中的 参数名1=参数值1&参数名2=参数值2 的形式。很明显,此种方式只是将文件名称通过post方式传输给服务器,仅有名称而无文件信息,这样是无法上传文件的。
接下来看第二类代码
<form action="handle.php" method="post" enctype="multipart/form-data">
<input type="text" name="uname" class="uname" /><br />
<input type="text" name="email" class="email" /><br />
<input type="file" name="file" class="file" /><br />
<input type="submit" name="submit" value="提交"/>
</form>
和第一段代码唯一不同的地方是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表单上传文件的程序来说没有什么帮助,但是在我看来,多了解一些细节对于我们以后编写类似的程序会有很大的帮助,毕竟对于我们程序员来说要知其然,更要知其所以然。
由于本人水平有限,无法触及到更深层次的知识,本篇权当抛砖引玉,希望有高手指教,如有兴趣请在下方留言,共同讨论,共同进步
相关文章
使用 Docker 网络主机命令
发布时间:2023/04/18 浏览次数:111 分类:Docker
-
在本文中,我们将学习如何使用 --network 命令将容器添加到主机网络。 如果我们不使用此命令指定网络,我们还将了解如何将容器添加到默认网络。
使用 Docker Postgres 创建数据库用户
发布时间:2023/04/17 浏览次数:97 分类:Docker
-
本文说明了可用于使用 Docker Postgres 创建数据库用户的不同方法。
在 Docker Compose 中添加网络模式
发布时间:2023/04/17 浏览次数:84 分类:Docker
-
默认情况下,单个网络由 Docker Compose 在我们的应用程序中创建,并将每个容器作为服务添加到那里。 网络上的每个容器都可以被单个网络上的容器访问和找到。
在 Angular 中上传文件
发布时间:2023/04/14 浏览次数:69 分类:Angular
-
本教程演示了如何在 Angular 中上传任何文件。我们还将介绍如何在文件上传时显示进度条,并在上传完成时显示文件上传完成消息。
在 Angular 中上传图片
发布时间:2023/04/14 浏览次数:165 分类:Angular
-
本教程演示了如何在 Angular 中上传图片。图像是任何 Web 应用程序的基本组成部分,每个网站都有图像。在本教程中,我们将学习如何以不同的方式在 Angular 中上传图像。
检查 PHP 中是否存在某 Post
发布时间:2023/03/29 浏览次数:112 分类:PHP
-
本文将教你如何使用 isset()、empty()、否定运算符和空字符串检查来检查 PHP 中是否存在 post