1. 程式人生 > >Spring 梳理-處理Multipart 請求

Spring 梳理-處理Multipart 請求

  1. 原理講解
    1. 簡單的HTTP POST
      
      大家通過HTTP向伺服器傳送POST請求提交資料,都是通過form表單提交的,程式碼如下:
      
      <form method="post"action="http://w.sohu.com" >
      
               <inputtype="text" name="txt1">
      
               <inputtype="text" name="txt2">
      
       </form>
      
      提交時會向伺服器端發出這樣的資料(已經去除部分不相關的頭資訊),資料如下:
      
       
      
      POST / HTTP/1.1
      Content-Type:application/x-www-form-urlencoded Accept-Encoding: gzip, deflate Host: w.sohu.com 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 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表單提交檔案操作如下: <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) •IE和Chrome在filename的選擇策略上有所不同,前者是檔案的完整路徑,而後者則僅僅是檔名。 •資料內容以兩條橫線結尾,並同樣以一個換行結束。在網路協議中一般都以連續的CR、LF(即\r、\n,或0x0D、Ox0A)字元作為換行,這與Windows的標準一致。如果您使用其他作業系統,則需要考慮它們的換行符。

       

  2. 後臺處理
    1. 使用multipart/form-data提交的資料使用HttpServletRequest物件的getParameter()等方法無法讀取。可以讀取整個請求體資料流自己解析資料。但更好的方式是使用已經有的第三方工具類,如fileupload,jspsmartupload。下面的例子中使用的是fileupload。
    2. HttpServletRequeest request=....
      if(ServletFileUpload.isMultipartContent(request)) 
      {
          FileItemFactory factory = new DiskFileItemFactory();
          ServletFileUpload upload = new ServletFileUpload(factory);
          List<FileItem> items = upload.parseRequest(request);
          for(FileItem i: items)
          {
              i.getFieldName();    //引數名
              //i.getString();     //引數值(返回字串),如果是上傳檔案,則為檔案內容
           //i.get();           //引數值(返回位元組陣列),如果是上傳檔案,則為檔案內容
           //i.getSize();     //引數值的位元組大小
           //i.getName();       //上傳檔案的檔名
           //i.getContentType();  //上傳檔案的內容型別
           if(!i.isFormField()&&i.getSize()>0)   //簡單引數返回true,檔案返回false 
            Files.write(Paths.get("/upload/"+Paths.get(i.getName()).getFileName()), i.get());      
        }
      }