5

概述

文件上传是一个很常见的需求,实现文件上传的技术也很多。下面就谈谈一些常见的上传技术以及它们的优劣。

传统表单上传

传统表单文件上传估计是运用最广泛和最简单的技术了,说它简单是因为只要指定表单的enctypemultipart/form-data,就行了。简单可靠所以被运用的广泛。传统表单上传示例如下所示:

<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>

表单中的action参数指的是后台处理上传文件的接口。target参数规定了规定在何处打开 action URL,常见的参数有_blank、_self、__top或者指定的iframe。method参数规定了表单提交的方式,这里只能使用**post**方式提交,而不能用**get**方式。最后enctype的参数规定了在发送到服务器之前应该如何对表单数据进行编码方式,常见的参数有**application/x-www-form-urlencoded**、**text/plain**和示例中的**multipart/form-data**,因为上传的文件都是非纯文本传输,所以指定的类型必须只能是**multipart/form-data**。

这种方式的局限性是不能批量处理,而且表单上传是同步的,表单已提交,页面就会刷新。

第三方插件处理文件上传

如果你想实现异步提交文件,且可以进行批量处理文件,那么只能通过第三方插件来实现了,第三方插件实现技术有很多,比如Flash、java applet、ActiveX等技术,其中Flash技术是运用最广泛、最成熟的一种方案。

不过第三方插件已经不属于前端开发范围了,所以不会详细细说。不过到时可以我常用的FLash上传插件有SWFupload、uploadfile、百度的webupload等。

第三方插件的好处是能做批量处理、异步提交。缺点也是显而易见的是要浏览器支持。

模拟异步上传文件

说到异步,可能有人会说,能不能通过ajax来实现文件的异步上传呢?想法很美好,现实却很残酷,ajax与后端通信 只能传送字符串,是无法传递实体文件的,所以用ajax是无法实现直接文件上传的。不过我们却可以在页面“埋”一个隐藏iframe来模拟文本的异步提交。

具体的原理是,在点击提交按钮时,动态的生成一个隐藏iframe加入到页面上,并且 将form 的 target指向该隐藏iframe,服务端就接收到上传的file文件,并进行相应的操作,然后将返回结果返回到隐藏的iframe里面,这时我们可以与后端开发约定返回结果的格式,可以是json格式,便于我们前端操作,然后前端部分就可以用iframe.contentWindow.document.body.innerHTML来获取后端返回的结果,进行相应的parseJSON处理,即可像处理ajax返回的json一样,处理数据。

示例代码如下所示:

/**
 * 模拟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('上传后');
    }
}); 

这种方式的好处是,虽然不是异步提交,但是给人的感觉好像是通过异步方式上传了文件。缺点是依然不能进行批量处理文件。

使用FormData对象发送文件

XMLHttpRequest Level 2添加了一个新的接口FormData.利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()方法来异步的提交这个"表单".比起普通的ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件.其兼容性如下所示:

图片描述

有图可知,这个接口兼容性在IE上表现的并不是很好,最新只支持IE10+,不过IE10+的市场份额现在还不是很多,如果你考虑兼容性的话,建议不要使用这个接口。

那怎样通过FormData上传文件呢。可以参考下面的代码。
html结构:

<form enctype="multipart/form-data" method="post" name="fileinfo">
  <label>Your email address:</label>
  <input type="email" autocomplete="on" autofocus name="userid" placeholder="email" required size="32" maxlength="64" /><br />
  <label>Custom file label:</label>
  <input type="text" name="filelabel" size="12" maxlength="32" /><br />
  <label>File to stash:</label>
  <input type="file" name="file" required />
</form>
<div id="output"></div>
<a href="javascript:sendForm()">Stash the file!</a>   

脚本代码:

function sendForm() {
  var oOutput = document.getElementById("output");
  var oData = new FormData(document.forms.namedItem("fileinfo"));

  oData.append("CustomField", "This is some extra data");

  var oReq = new XMLHttpRequest();
  oReq.open("POST", "stash.php", true);
  oReq.onload = function(oEvent) {
    if (oReq.status == 200) {
      oOutput.innerHTML = "Uploaded!";
    } else {
      oOutput.innerHTML = "Error " + oReq.status + " occurred uploading your file.<br \/>";
    }
  };

  oReq.send(oData);
}    

这个属性弥补了ajax1不能异步上传文件的不足。

帮助文档

模拟AJAX无刷新的文件上传功能
页面无刷新ajax上传文件--模拟iframe,超简单
为什么上传文件的表单里要加个属性enctype
FormData接口对象的介绍
使用FormData对象

ps:涉及文章侵权,请邮件告知。


两仪
9.6k 声望729 粉丝

向上努力、不卑不亢、两仪相生