表單提交 multipart/form-data 和 x-www-form-urlencoded的區別


表單提交
表單有兩種提交方式,POST和GET。通常我們會使用POST方式,一是因為形式上的安全 ;二是可以上傳文件。

我之前經常忽略掉表單的編碼類型,覺得它特別長比較難記,而且不設置也似乎不影響什么。表單的編碼類型,用來控制表單中的數據的編碼格式。POST 提交方式 默認 enctype=“application/x-www-form-urlencoded”,數據以鍵值對的方式傳送到服務器,這種方式適合於大多數場景。GET 提交方式,默認none。

表單(POST請求)支持下面兩種編碼:

enctype
application/x-www-form-urlencoded 不指定時默認方式, key1=value1&key2=value2
multipart/form-data 一般用於表單需要文件上傳
表單(GET請求)支持下面兩種編碼:

enctype
application/x-www-form-urlencoded key1=value1&key2=value2
none 不指定時默認方式,getContentType() 返回null
ServletRequest中獲取參數常用方法:

Map<String,String[]> maps= request.getParameterMap(); //獲取所有的鍵值對
Enumeration<String> names= request.getParameterNames();//獲取所有的參數名
String[] values = request.getParameterValues(parameterName); //獲取某個參數下的所有值,適合多選組件
String value = request.getParameter(parameterName); //獲取某個參數對應的值,適合文本組件,單選等
ServletInputStream stream=request.getInputStream();//需要注意的是,該方法只能被調用一次,再次調用返回結果為空
BufferedReader reader=request.getReader();//該方法也是只能調用一次
Collection<Part> parts = request.getParts();// 獲取 multipart/form-data 編碼方式的數據

需要額外注意的是:getInputStream 方法和getReader方法互斥。最多只能調用其中的一個,如果這兩個方法都被調用則會拋出異常。

request.getInputStream();//需要注意的是,該方法只能被調用一次,再次調用返回結果為空
request.getReader();//該方法也是只能調用一次
1.application/x-www-form-urlencoded
  1. GET方式,會將表單中的數據(鍵值對)經過urlencode編碼后追加到url中。
  2. POST方式,會將表單中的數據經過urlencode編碼后放在request body 中。
    <form action="/xxxx" method="post"  enctype="application/x-www-form-urlencoded">
      name: <input type="text" name="name"><br>
      password: <input type="text" name="password"><br>
      <input type="submit" value="提交">
    </form>

    POST 提交方式: name 傳遞 “測試人”,password 傳遞“test”.(postman 工具測試結果)

    Content-Type: application/x-www-form-urlencoded
    
    name:%E6%B5%8B%E8%AF%95%E4%BA%BA
    password:test

    該編碼方式,會將表單數據中的非西歐字符轉化為十六進制數字的形式。utf8字符集中,每個漢字占3個字節,每個漢字會轉化成3個十六進制的數 %XX%XX%XX 的形式。

    在java 代碼中經常會處理下載文件名中文亂碼的問題,對於IE,谷歌等瀏覽器用到的就是編碼方式。

    try {
                System.out.println(URLEncoder.encode("測試人","UTF8"));
                 //%E6%B5%8B%E8%AF%95%E4%BA%BA
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

    當然瀏覽器不同使用的處理編碼也不盡相同。在使用Chrome的話顯示如下(gb2312編碼),這個可能和當前使用的語言環境有關:

    name: %B2%E2%CA%D4%C8%CB
    password: test

    接下來就是獲取值

    Map<String,String[]> maps= request.getParameterMap(); //獲取所有的鍵值對
    Enumeration<String> names= request.request.getParameterNames();//獲取所有的參數名
    String[] values = request.getParameterValues(parameterName); //獲取某個參數下的所有值,適合多選組件
    String value = request.getParameter(parameterName); //獲取某個參數對應的值,適合文本組件,單選等

    如果需要在表單中上傳文件則不能使用該方式

2.multipart/form-data

當需要在表單內上傳文件時(二進制流數據)時,就需要使用 multipart/form-data。

<form action="/xxxx" method="POST"  enctype="multipart/form-data">
      <label>手機號</label><input type="text" name="phone"><br>
    <label>郵箱</label> <input type="text" name="mail"><br>
     <label>頭像</label><input type="file" name="portrait"><br/>
      <input type="submit" value="提交">
</form>

請求行的部分內容

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

請求體的內容

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name=“phone”
16666666666
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name=“mail”
test@163.com
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="portrait "; filename=“portrait.png”
Content-Type: image/png
------WebKitFormBoundary7MA4YWxkTrZu0gW–

 

multipart,顧名思義 多部分。使用該編碼方式會將表單進行分割成每個控件(以----boundary為分隔符,將分割控件數據分隔開,在最后以—boundary—結尾)。每個部分必須加上Content-Disposition(form-data) ,對於上傳文件還會設置Content-Type。

對於上傳的不同類型的文件,會自動識別文件類型,如果識別不了則設置為application/octet-stream。

上傳jpeg格式圖片

Content-Type: image/jpeg

上傳pdf文件

Content-Type: application/pdf

上傳MP3

Content-Type: audio/mp3

對於使用該方式提交的表單,在服務器端的參數獲取通常有以下兩種:

單獨獲取 鍵值對數據。 使用getParameterMap() ,getParameter()等常規方法獲取數據
獲取全部數據(鍵值對和二進制流) getParts()。
使用getParts()方法,要求Content-Type:multipart/form-data,否則拋出異常。
通過Part 獲取上傳的文本內容

         
         System.out.println(request.getParameter("phone"));
         System.out.println(request.getParameter("mail"));
         System.out.println(request.getParameter("portrait"));//二進制數據返回null
         System.out.println("----------------getParts獲取參數-------------------------");
        //如果是multipart/form-data 提交方式,則此時 contentType:multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
        String contentType=request.getContentType();
        
        if(contentType !=null && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {//判斷是否是multipart/form-data 提交方式
            Collection<Part> parts = request.getParts(); //獲取所有的部分
            for(Part part :parts) {
                System.out.println(part.getContentType());
                InputStream  inPart = part.getInputStream();
                String name=part.getName();//請求名稱
                StringBuilder buffer=new StringBuilder(); //保存請求值
                byte[] buff =new byte[1024];
                int length=0;
                while((length=inPart.read(buff))!=-1) {
                    buffer.append(new String(buff,0,length,"utf8"));
                }
                System.out.println("name = "+name+"\tvalue="+buffer);
            }
        }

輸出結果

16666666666
test@163.com
null
----------------getParts獲取參數-------------------------
null
name = phone    value=16666666666
null
name = mail    value=test@163.com
image/png
name = portrait    value=�PNG

圖片比較大,所以只截取了一點。

注意:
在判斷contentType時,multipart/form-data提交方式的contentType不單純是multipart/form-data,還會包含 boundary=----WebKitFxxxx

小結
使用不同的編碼方式,服務端獲取參數的方式也不盡相同。

application/x-www-form-urlencoded。 該方式就是常規的方法, getParameterMap(),getParameter()等方式獲取參數

multipart/form-data

鍵值對 二進制數據
1 getParameter(),getParameterMap getParts();
2 getParts()  

注意: 使用 getParts() 方法 必須保證 ContentType 包含 ”multipart/form-data“ ,否則拋出ServletException,如果上傳文件特別大則會拋出IllegalStateException

重復說明:

getReader()和getInputSteam()互斥
getReader()和getInputStream() 只能調用一次
補充
在使用elasticsearch 的時候,發現 GET請求可以有請求體。當時就很懵逼,查閱資料后發現http標准中 GET請求確實可以有請求體。之前使用POSTMAN的時候都沒有注意過,也是心大了。

 

請求體的編碼方式和POST請求一致,但是GET方式請求體可以傳輸的數據要遠小於POST 請求的。

 

enctype   GET 請求獲取數據  POST 請求獲取數據
application/x-www-form-urlencoded    getInputStream()  getParameter()
multipart/form-data getInputStream()     getParameter()  getParameter(), getParts()
raw  比如常用的 ajax請求 application/json getInputStream()  getInputStream()
binary  文件類型 getInputStream() getInputStream()

 

注意:
1. getInputStream() 不限 getInputStream方式,還包括 getReader()
2. getParameter() 方式還包括 getParameterMap(),getParameterValues(),getParameterNames()
3. multipart/form-data 類型的請求體, GET請求的數據不能通過getParts()獲取
測試發現,雖然GET請求可以使用上面的幾種方式。但是請求體中的內容只能通過 getInputStram()等流式方式獲取。

文本
可以指定的文本內容格式有多種,application/json是最常用的

text/plain: 純文本
text/xml :傳遞xml語法格式的字符串
text/html: 傳遞html字符串
application/json : 傳遞序列化后的 JSON 字符串。
獲取數據

ServletInputStream  in = request.getInputStream();//該方法也是只能調用一次
BufferedReader reader=request.getReader();//該方法也是只能調用一次

文本數據格式,會使用流的方式傳遞數據。在springmvc中使用@RequestBody注解對應的注解解釋類應該就是通過InputSteam獲取數據並轉化為json對象。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM