文件上传
概述
浏览器中文件上传,基本有两种方案,一种是表单上传,一种是通过ajax上传
一、表单上传
三要素
表单也就是form元素,如果要上传文件,必须满足上传三要素:
- method属性设置成
post- 文件上传的选项的type必须为
file- enctype属性必须为
multipart/form-data
对于纯粹的表单上传,这三要素是必须的,原因看我下面的实验例子
例子
我的上传表单是这样:
我会对 name 为 info 的输入框填入test
而文件我选择了test.txt,该文件的内容是一段话:hello word
满足三要素
表单保持例子原状,当我点击上传后,浏览器会把这个表单整合成一个 HTTP 报文,然后发送给相应的 action路径,比如上述的会生成如下 HTTP 请求报文(我已经删除了多余的请求头信息,只保留了重点的一行Content-Type):
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary51R26gaG9hSSCrHF
----WebKitFormBoundary51R26gaG9hSSCrHF
Content-Disposition: form-data; name="info"
test
----WebKitFormBoundary51R26gaG9hSSCrHF
Content-Disposition: form-data; name="upload"; filename="test.txt"
Content-Type: text/plain
hello word
----WebKitFormBoundary51R26gaG9hSSCrHF-
注意该报文是浏览器根据 from 的enctype 属性自动生成的,不需要人控制。
解析报文中的Content-Type:
multipart/form-data是编码格式,boundary 是分割线格式,主要用途就是为了请求体的分割
所以为什么需要设置 POST 请求,原因也在此,只有表单设置为 POST ,浏览器才会将表单数据放入请求体。而设置了 enctype 为multipart/form-data,浏览器才会对 HTTP 请求报文进行相应的操作。
不满足三要素
接下来的阐述都只针对于 form 的 method 和 enctype 进行阐述。
method 为 GET 请求
将例子的表单,method 设置成 GET 请求。
那么浏览器会将表单数据存放在报文的请求行,而不是请求体。
所以不管表单的 enctype 设置成什么,是完全影响不到请求行的,所以 GET 请求的请求行始终不变,报文永远为下面的样子(同样删除多余的请求头信息):
GET /upload?info=test&upload=test.txt HTTP/1.1
Content-Type: application/x-www-form-urlencoded
POST 请求
将例子的表单属性 enctype 设置成其它属性,比如不设置(不设置那么 enctype 属性默认值为:"application/x-www-form-urlencoded"),则 POST 请求报文为:
POST /upload HTTP/1.1
Content-Type: application/x-www-form-urlencoded
info=test&upload=test.txt
会发现,upload 的值是个字符串,也就是test.txt,一个字符串,和 GET 请求一样的。
二、ajax上传
XHR2定义了全新的FormData对象,能够进行multipart/form-data编码的http请求。因此,这种方式上传文件,相当于使用表单上传文件。
这种请求方式现在很流行,一搜索基本都是教程,我就不讲了。
总结
表单上传,为什么要遵守三要素,因为浏览器默默帮你做了很多事情。除非 js 手段,否则必须准守这三个规则。
此外,并不是说 GET 方法不能上传,但是你得用js编码完成许多额外的工作,比如设置请求报文的请求头的Conten-Ttpe,自己完成对请求体的编码。
但是呢,用 js 操作,js 发送请求,这就属于ajax的范畴了。
而ajax上传,也有合适的解决方案,也就是通过FormData上传。
如果你很倔强,又或者有什么特殊的需求,也可以通过 GET 请求实现上传,也不是不可能,只是说约定如此,就像 RESTful 风格的请求。
对了,在stackoverflow上也早有对这个能否通过 GET 请求上传的探讨,链接在这:
File uploading using GET Method:As we all know, file uploading is most often accomplished using POST method. So, why can't the GET method be used for file uploads instead? Is there a specific prohibition against HTTP GET uploads?