AJAX 异步提交数据有很多优点,那么怎样在表单中使用 AJAX 异步上传图片呢。本文对关键技术进行讲解。

1. 完整代码

首先贴上正确代码,然后对其中的重要地方进行说明。

<input type="file" id="file">

<script src="jquery.min.js"></script>
<script>
    $("#file").change(function () {
        var file = document.getElementById("file").files[0]; // 只上传一个文件
        //var file = $("#file")[0].files[0]; // 和上面结果一样
        var formData = new FormData(); // 实例化一个 FormData 对象
        formData.append('file', file); // 按键值对组装数据

        $.ajax({
            url: 'http://seller.test/api/v1/image/upload',
            type: "POST",
            data: formData,
            processData: false,
            contentType: false,
            success: function (data) {
                console.info(data);
            }
        })
    });
</script>

2. 代码分析

2.1 关键地方

2.1.1 processData

类型:Boolean,默认为 true。默认情况下,通过 data 选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会转化成一个查询字符串,来配合默认的编码格式 application/x-www-form-urlencoded。如果要发送 DOM 树信息或者其它不希望转换的信息(比如二进制数据),需要将其设置为 false。

2.1.2 contentType

类型:String/Boolean,默认值 application/x-www-form-urlencoded,HTTP 协议发送内容时的编码格式。如果设置了这个值,将在请求头中显式的将其发送给服务器(即使没有数据要发送)。

2.1.3 FormData

利用 FormData 对象,我们可以通过 JavaScript 用一些键值对来模拟一系列表单控件。比起普通的 ajax,使用 FormData 的最大优点是我们可以异步上传一个二进制文件。

2.2 form 表单数据能以四种方式提交到服务器

  1. POST 请求,将 enctype 属性设置为 application/x-www-form-urlencode,即默认的传输格式

    Content-Type: application/x-www-form-urlencoded
    
    foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A
  2. POST 请求,将 enctype 属性设置为 text/plain

    Content-Type: text/plain
    
    foo=bar
    baz=The first line.
    The second line.
  3. POST 请求,将 enctype 属性设置为 multipart/form-data

    Content-Type: multipart/form-data; boundary=---------------------------314911788813839
    
    -----------------------------314911788813839
    Content-Disposition: form-data; name="foo"
    
    bar
    -----------------------------314911788813839
    Content-Disposition: form-data; name="baz"
    
    The first line.
    The second line.
    
    -----------------------------314911788813839--
  4. GET 请求

    类似 ?foo=bar&baz=The%20first%20line.%0AThe%20second%20line. 的参数会被添加到 URL 后面。

    这是 HTTP 协议的一部分,具体可以看见 HTTP 协议详解(三)

我们使用的是 multipart/form-data 格式来传输文件数据的,注意 Content-Type,值为 multipart/form-data,紧接着后面还有一个内容分界线标识,用来分隔数据,两个分隔线之间就是一个完整的数据对象,参看上面的代码示例。这就是为什么将 enctype 属性设置为 multipart/form-data 时,数据传输出错的原因,因为设置了 Content-Type 之后,显式的将值变成 multipart/form-data,而少了后面的分界线标识,造成服务的无法正确接收数据。其实看到这里,我们也可以根据 HTTP 协议自行封装文件上传的请求。