你们经过HTTP向服务器发送POST请求提交数据,都是经过form表单提交的,代码以下:html
<form method="post"action="http://w.sohu.com" >编程
<inputtype="text" name="txt1">浏览器
<inputtype="text" name="txt2">服务器
</form>网络
提交时会向服务器端发出这样的数据(已经去除部分不相关的头信息),数据以下:app
POST / HTTP/1.1post
Content-Type:application/x-www-form-urlencoded编码
Accept-Encoding: gzip, deflateurl
Host: w.sohu.comspa
Content-Length: 21
Connection: Keep-Alive
Cache-Control: no-cache
txt1=hello&txt2=world
对于普通的HTML Form POST请求,它会在头信息里使用Content-Length注明内容长度。头信息每行一条,空行以后即是Body,即“内容”(entity)。它的Content-Type是application/x-www-form-urlencoded,这意味着消息内容会通过URL编码,就像在GET请 求时URL里的QueryString那样。txt1=hello&txt2=world
最先的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表单提交文件操做以下:
<form method="post"action="http://w.sohu.com/t2/upload.do" enctype=”multipart/form-data”>
<inputtype="text" name="desc">
<inputtype="file" name="pic">
</form>
浏览器将会发送如下数据:
POST /t2/upload.do HTTP/1.1
User-Agent: SOHUWapRebot
Accept-Language: zh-cn,zh;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Length: 60408
Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Host: w.sohu.com
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="desc"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[......][......][......][......]...........................
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="pic"; filename="photo.jpg"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
[图片二进制数据]
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC--
咱们来分析下数据,第一个空行以前天然仍是HTTP header,以后则是Entity,而此时的Entity也比以前要复杂一些。根据RFC 1867定义,咱们须要选择一段数据做为“分割边界”( boundary属性),这个“边界数据”不能在内容其余地方出现,通常来讲使用一段从几率上说“几乎不可能”的数据便可。 不一样浏览器的实现不一样,例如火狐某次post的 boundary=---------------------------32404670520626 , opera为boundary=----------E4SgDZXhJMgNE8jpwNdOAX ,每次post浏览器都会生成一个随机的30-40位长度的随机字符串,浏览器通常不会遍历此次post的全部数据找到一个不可能出如今数据中的字符串,这样代价太大了。通常都是随机生成,若是你碰见boundary值和post的内容同样,那样的话此次上传确定失败,不过我建议你去买彩票,你太幸运了。Rfc1867这样说明{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)
另外Content-length 指的是所用数据的长度。
httpClient4使用http-mime.jar包的MultipartEntity实现,代码以下(为了简洁,处理了异常处理代码):
HttpPost httpPost = newHttpPost(url);
Log.debug("post url:"+url);
httpPost.setHeader("User-Agent","SOHUWapRebot");
httpPost.setHeader("Accept-Language","zh-cn,zh;q=0.5");
httpPost.setHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.7");
httpPost.setHeader("Connection","keep-alive");
MultipartEntity mutiEntity = newMultipartEntity();
File file = new File("d:/photo.jpg");
mutiEntity.addPart("desc",new StringBody("美丽的西双版纳", Charset.forName("utf-8")));
mutiEntity.addPart("pic", newFileBody(file));
httpPost.setEntity(mutiEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
String content = EntityUtils.toString(httpEntity);
参考:
Rfc1867:http://www.ietf.org/rfc/rfc1867
Rfc1867:http://www.vivtek.com/rfc1867.html