1. 程式人生 > >通過POST請求上傳檔案

通過POST請求上傳檔案

轉自:https://blog.csdn.net/zhangge3663/article/details/81218488

理論

簡單的HTTP POST

大家通過HTTP向伺服器傳送POST請求提交資料,都是通過form表達提交的,程式碼如下:

  1. <form method="post" action="http://w.sohu.com">
  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.com
  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="http://w.sohu.com/t2/upload.do" enctype="multipart/form-data">
  2. <input type="text" name="desc">
  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.com
  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 = httpClient. execute(httpPost);
  15. HttpEntity httpEntity = httpResponse.getEntity();
  16. String content = EntityUtils.toString(httpEntity);