文件上传

文件上传

简单来说,文件上传的方式可以分为两种

  • 同步上传文件(页面锁死,需要等待文件上传完毕,上传完之后页面需要刷新)
  • 异步上传

同步上传文件

传统的上传文件方式就是form表单上传,这是属于同步上传。

使用方法很简单 ,只需要指定表单的 enctype为multipart/form-data

1
2
3
4
<form action="test.php" target="" method="post" enctype="multipart/form-data">
<input type="file "name="file" id="file" />
<input type="submit" id="J_submit" value="submit" />
</form>
  • 这里method,上传方式只能选择 post 。
  • enctype这个属性管理的是表单的MIME,参数规定了在发送到服务器之前应该如何对表单数据进行编码方式。
  • 常见的参数有application/x-www-form-urlencodedtext/plain和示例中的multipart/form-data,因为上传的文件都是非纯文本传输,所以指定的类型必须只能是multipart/form-data
    • 其中①application/x-www-form-urlencoded是默认值,
      大家可能在AJAX里见过这个:xmlHttp.setRequestHeader(“Content-Type”,”application/x-www-form-urlencoded”);
      这两个要做的是同一件事情,就是设置表单传输的编码。在AJAX里不写有可能会报错,但是在HTML的form表单里是可以不写enctype=”application/x-www-form-urlencoded”的,因为默认HTML表单就是这种传输编码类型。
      • 而②multipart-form-data是用来指定传输数据的特殊类型的,主要就是我们上传的非文本的内容,比如图片或者mp3等等。
      • ③text/plain是纯文本传输的意思,在发送邮件时要设置这种编码类型,否则会出现接收时编码混乱的问题,网络上经常拿text/plain和text/html做比较,其实这两个很好区分,前者用来传输纯文本文件,后者则是传递html代码的编码类型,在发送头文件时才用得上。
      • ①和③都不能用于上传文件,只有multipart/form-data才能完整的传递文件数据。

用户选择文件后,点击提交按钮即可上传文件。但是点击开始上传文件后,页面会锁死,并在上传完毕后刷新页面。这样的上传方式局限还有不能多文件上传。

异步上传文件

说到异步,可能会有人说ajax能不能实现文件的异步上传呢?答案是不行,ajax与后端通信只能传送字符串,是无法传递实体文件的,所以用ajax无法实现直接文件上传,不过我们可以在页面插入一个iframe来模拟文本的异步提交。

iframe上传

具体的原理就是,在点击提交按钮时,动态的生成一个隐藏的iframe加入页面上,并且把formtarget指向隐藏的iframe,服务端就可以收到我们上传的文件,并进行相应的操作,然后将操作结果返回给隐藏的 iframe,,然后前端部分可以用 iframe.contentWindow.document.body.innerHTML来获取后端返回的结果,进行相应的JSON.parse处理数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*
* 需要引入jq 文件
* 模拟ajax无刷新文件上传
*/

var fileUpLoad = function(config) {

var ifr = null,
fm = null,
defConfig = {
submitBtn: $('#J_submit'), //提交按钮
complete: function(response) {}, //上传成功后回调
beforeUpLoad: function() {}, //点击提交未上传时回调
afterUpLoad: function() {} //点击提交上传后回调
};

//静态变量
var IFRAME_NAME = 'fileUpLoadIframe';

//配置
config = $.extend(defConfig, config);

//绑定submit事件
config.submitBtn.bind('click', function(e){
e.preventDefault();

//点击提交前触发事件, 函数返回false可阻止提交表单,用于file为空判断
if (config.beforeUpLoad.call(this) === false) {
return;
}

//生成一个隐藏iframe,并设置form的target为该iframe,模拟ajax效果
ifr = $('<iframe name="'+ IFRAME_NAME +'" id="'+ IFRAME_NAME +'" style="display:none;"></iframe>');
fm = this.form;

ifr.appendTo($('body'));
fm.target = IFRAME_NAME; //target目标设为ifr
//上传完毕iframe onload事件
ifr.load(function(){
var response = this.contentWindow.document.body.innerHTML;

config.complete.call(this, response);
ifr.remove();
ifr = null; //清除引用
});

fm.submit(); //提交表单
//点击提交后触发事件
config.afterUpLoad.call(this);

});

};

// 调用方式如下:
fileUpLoad({
submitBtn: $('#J_submit'),
complete: function(response){ //上传成功后处理回调
var d = $.parseJSON(response);

alert('返回成功')
console.log(d);
},
beforeUpLoad: function() {
alert('上传前');
},
afterUpLoad: function() {
alert('上传后');
}
});

缺点也是不能批量处理。

使用FromData对象发送文件

HTML5提出了XMLHttpRequest对象的第二版,从此ajax能够上传文件了。这是真正的”异步上传”,是将来的主流。上一节的iframe上传,可以用作老式浏览器的替代方案。
image

  • 在使用 FormData 异步上传文件之前,让我们先来了解下 FormData 的相关知识。
  1. 初始化 FormData 对象

    • 先创建一个空的FormData对象,然后使用append()方法向该对象里添加字段

      1
      2
      3
      4
      var oMyForm = new FormData();
      oMyForm.append("username", "Groucho");
      // fileInputElement中已经包含了用户所选择的文件
      oMyForm.append("userfile", fileInputElement.files[0]);
    • 使用HTML表单来初始化一个FormData对象

      1
      2
      var formElement = document.getElementById("myFormElement");
      var oReq = new XMLHttpRequest();
  2. append() :给当前FormData对象添加一个键/值对.

    1
    2
    oid append(DOMString name, Blob value, optional DOMString filename);
    void append(DOMString name, DOMString value);
    • name:字段名称.
      value:字段值.可以是,或者一个字符串,如果全都不是,则该值会被自动转换成字符串.
      filename: (可选)指定文件的文件名,当value参数被指定为一个Blob对象或者一个File对象时,该文件名会被发送到服务器上,对于Blob对象来说,这个值默认为”blob”.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      var oMyForm = new FormData();

      oMyForm.append("username", "Groucho");
      oMyForm.append("accountnum", 123456); // 数字123456被立即转换成字符串"123456"

      // fileInputElement中已经包含了用户所选择的文件
      oMyForm.append("userfile", fileInputElement.files[0]);

      var oFileBody = '<a id="a"><b id="b">hey!</b></a>'; // Blob对象包含的文件内容
      var oBlob = new Blob([oFileBody], { type: "text/xml"});

      oMyForm.append("webmasterfile", oBlob);
  • 使用FormData对象上传文件实例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /*
    * ajax上传代码,放在表单的submit事件回调函数中:
      form.on('submit',function() {
        // 此处进行ajax上传
    fileUpload.bind($(this))
      });
    *
    */
    //我们主要用的是FormData对象,它能够构建类似表单的键值对。
    function fileUpload() {
    // 检查是否支持FormData
      if(window.FormData) { 
        var formData = new FormData();
        // 建立一个upload表单项,值为上传的文件
        formData.append('upload', document.getElementById('upload').files[0]);
    //另一种使用HTML表单来初始化初始化FormData对象
    // var formData = new FormData( $(this) );
    // formData.append('upload')

        var xhr = new XMLHttpRequest();
        xhr.open('POST', $(this).attr('action'),true);
        // 定义上传完成后的回调函数
        xhr.onload = function () {
          if (xhr.status === 200) {
            console.log('上传成功');
          } else {
            console.log('出错了');
          }
        };
        xhr.send(formData);
      }
    }

资料参考:

为什么上传文件的表单里要加个属性enctype
web端文件上传功能的思考
FormData|MDN
文件上传的渐进式增强