通过POST请求上传⽂件
理论
简单的HTTP POST
⼤家通过HTTP向服务器发送POST请求提交数据,都是通过form表达提交的,代码如下:
1. <form method="post" action="w.sohu">
2. <input type="text" name="txt1">
3. <input type="text" name="txt2">
4. </form>
提交时会向服务器段发出这样的数据(已经去除部分不相关的头信息),数据如下:
1. POST / HTTP/1.1
2. Content-Type:application/x-www-form-urlencoded
3. Accept-Encoding: gzip, deflate
4. Host: w.sohu
5. Content-Length: 21
6. Connection: Keep-Alive
7. Cache-Control: no-cache
8.
9. txt1=hello&txt2=world
10.
对于普通的HTML Form POST请求,它会在头信息⾥使⽤Content-Length注明内容长度。头信息每⾏⼀条,空⾏之后便是Body,即"内容" (entity)。它的Content-Type是application/x-www-form-urlencoded,这意味着消息内容会经过URL编码,就像在GET请求时URL⾥的QueryString那样。txt1=hello&txt2=world.
POST上传⽂件
最早的HTTP POST是不⽀持⽂件上传的,给编程开发带来很多问题。但是在1995年,ietf出台了rfc1867,也就是<RFC 1867-Form-based File Upload in HTML>,⽤以⽀持⽂件上传。所以Content-Type的类型扩充了multipart/form-data⽤以⽀持向服务器发送⼆进制数据。因此发送post请求时候,表单<form>属性enctype共有两个值可选,这个属性管理的是表单的MIME编码:
①application/x-www-form-urlencoded(默认值)
②multipart/form-data
其实form表单在你不写enctype属性时,也默认为其添加了enctype属性值,默认值是enctype="application/x-www-form-urlencoded".
通过form表单提交⽂件操作如下:
1. <form method="post" action="w.sohu/t2/upload.do" enctype="multipart/form-data">
2. <input type="text" name="desc">inputtypefile不上传文件
3. <input type="file" name="pic">
4. </form>
浏览器将会发送以下数据:
1. POST /t2/upload.do HTTP/1.1
2. User-Agent: SOHUWapRebot
3. Accept-Language: zh-cn,zh;q=0.5
4. Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
5. Connection: keep-alive
6. Content-Length: 60408
7. Content-Type: multipart/form-data;boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
8. Host: w.sohu
9.
10. --ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
11. Content-Disposition: form-data;name="desc"
12. Content-Type: text/plain; charset=UTF-8
13. Content-Transfer-Encoding: 8bit
14.
15. [......][......][......][......]..........
16. --ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
17. Content-Disposition: form-data;name="pic";filename="photo.jpg"
18. Content-Type: application/octet-stream
19. Content-Transfer-Encoding: binary
20.
21. [图⽚⼆进制数据]
22. --ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC--
23.
我们来分析下数据,第⼀个空⾏之前⾃然还是HTTP header,之后则是Entity,⽽此时的Entity也⽐之前要复杂⼀些。根据RFC 1867定义,我们需要选择⼀段数据作为"分隔边界"(boundary属性),这个"边界数据"不能在内容其他地⽅出现,⼀般来说使⽤⼀段从概率上说"⼏乎不可能"的数据即可。不同的浏览器的实现不同,例如⽕狐某次post的boundary=---------------------------32404670520626 ,opera为 boundary=----------E4SgDZXhJMgNE8jpwNdOAX ,每次post浏览器都会⽣成⼀个随机的30-40位长度的随机字符串,浏览器⼀般不会遍历这次post的所有数据到⼀个不可能出现的数据中的字符串,这样代价太⼤了。⼀般都是随机⽣成,如果你遇见boundary值和post的内容⼀样,那样的话这次上传肯定失败,不过我建议你去买,你太幸运了。rfc 1867这样说明{A boundary is selected that does not occur in any of the data.(This selection is sometimes done probabilisticly.)}。
选择了这个边界之后,浏览器便把它放在Content-Type⾥⾯传递给服务器,服务器根据此边界解析数据。下⾯的数据便根据boundary划分段,每⼀段便是⼀项数据。(每个field被分成⼩部分,⽽且包含⼀个value是"form-data"的“Content-Disposition"的头部;⼀个"name"属性对应field的ID,等等,⽂件的话包括
⼀个filename).
IE和Chrome在filename的选择策略上有所不同,前者是⽂件的完整路径,⽽后者则仅仅是⽂件名。
数据内容以两条横线结尾,并同样以⼀个换⾏结束。在⽹络协议中⼀般都以连续的CR、LF(即\r、\n,或0x0D, 0x0A)字符作为换⾏,这与Windows的标准⼀致。如果您使⽤其他操作系统,则需要考虑他们的换⾏符。
另外Content-length指的是所⽤数据的长度。
实现
httpClient4如何实现
httpClient4使⽤http-mime.jar包的MultipartEntity实现,代码如下:
1. HttpPost httpPost = newHttpPost(url);
2. Log.debug("post url:"+url);
3. httpPost.setHeader("User-Agent", "SOHOWapRebot");
4. httpPost.setHeader("Accept-Language", "zh-cn,zh;q=0.5");
5. httpPost.setHeader("Accept-Charset", "GBK,utf-8;q=0.7,*;q=0.7");
6. httpPost.setHeader("Connection", "keep-alive");
7.
8. MultipartEntity multiEntity = new MultipartEntity();
9. File file = new File("d:/photo.jpg");
10. multiEntity.addPart("desc", new StringBody("美丽的西双版纳", Charset.forName("utf-8")));
11. multiEntity.addPart("pic", newFileBody(file));
12.
13. httpPost.setEntity(multiEntity);
14. HttpResponse httpResponse = ute(httpPost);
15. HttpEntity httpEntity = Entity();
16. String content = String(httpEntity);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论