你們經過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