异步提交表单对于现代AJAX程序来说是一件轻而易举的事,如果使用jQuery等库的话更无需考虑浏览器的差异,简单的一句$.post 搞定,但如果要上传文件呢?jQuery的异步请求方法并没有提供异步提交文件的功能,这个时候我们可以使用jQuery的Form插件(http://malsup.com/jquery/form/)来完成这个功能,或者我们可以搞清楚原理,自己实现一个。

使用jQuery的form插件实现异步提交文件

使用form插件实现异步提交表单非常简单,只需要针对form表单执行ajaxForm初始化即可

<form id="myForm" action="/upload" method="post" enctype="multipart/form-data">
  描述:<input name="desc">
  文件:<input name="file" type="file">
  <button id="submit" type="submit">提交</button>
</form>
<script>
$('#myForm').ajaxForm(function() {
  alert("提交成功");
});
</script>

 XMLHttpRequest2对异步提交表单的支持

现代的浏览器对异步请求提供了功能更强大的支持,这就是我们要隆重介绍的XMLHttpRequest2。顺便提一句,XMLHttpRequest2并不属于HTML5,它是个新的、功能强大的API。我们可以构造XMLHttpRequest2的FormData对象并放进去我们想要的文本、文件等。

$('#myForm').on('submit', function() {
    var formElem = this;
    var formData = new FormData();
    $('#myForm').find('input').each(function() {
      if (this.type=='file' && this.files.length>0) {
        for (var i= 0; i<this.files.length; i++) {
          formData.append(this.name, this.files.item(i));
        }
      } else {
        formData.append(this.name, this.value);
      }
    });

    $.ajax({
      url: formElem.action,
      data: formData,
      processData: false,
      contentType: false,
      type: formElem.method,
      success: function(data){
        alert(data);
      }
    });

    return false;
  });

 低级浏览器下的解决方法

XMLHttpRequest2只有IE10+才支持,那么对于低版本的浏览器如何实现呢?我们可以构建一个虚拟表单,并提交到iframe里的方式。

$('#myForm').on('submit',function() {
    var jQorigForm = $(this);

    // 创建一个用于存放结果的iframe
    var iframeName = 'form-target-'+new Date().valueOf();
    var jQiframe = $('<iframe id="'+iframeName+'" name="'+iframeName+'" style="position:absolute;left:-9999px;top:-9999px"></iframe>')
      .appendTo('body');

    // 创建一个临时表单,令其提交到iframe中
    var jQform = jQorigForm.clone()
      .html('')
      .appendTo('body')
      .attr('target',iframeName)
      .css({position:'absolute',left:'-9999px',top:'-9999px'});

    // 拷贝输入框到临时表单
    jQorigForm.find('input').each(function() {
      var jQinput = $(this);
      var jQnewInput = jQinput.clone();
      if (this.type=='file') { // 由于FileInput不能赋值,所以只能交换两个form的元素的权宜之计
        jQinput.before(jQnewInput);
        jQinput.appendTo(jQform);
      } else {
        jQnewInput.appendTo(jQform);
      }
    });

    // 提交临时表单
    jQform.submit().remove();

    // 设个定时从iframe中读取结果
    var interval = setInterval(function() {
      var content = jQiframe.contents().find('body').html();
      if (content) {
        alert(content);
        jQiframe.remove();
        clearInterval(interval);
      }
    }, 400);

    return false;
  });

参考资料