zoukankan      html  css  js  c++  java
  • 表单提交 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+"	value="+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对象。

  • 相关阅读:
    结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
    深入理解系统调用
    基于mykernel2.0编写一个操作系统内核
    如何评测一个软件工程师的计算机网络知识水平与网络编程技能水平?
    如何评测软件工程知识技能水平?
    深入理解TCP协议及其源代码
    Socket与系统调用深度分析
    创新产品的需求分析:未来的图书会是什么样子?
    构建调试Linux内核网络代码的环境MenuOS系统
    解决npm ERR! code ELIFECYCLE npm ERR! errno 1问题
  • 原文地址:https://www.cnblogs.com/Im-Victor/p/11327789.html
Copyright © 2011-2022 走看看