文件上传相关的事儿

时间:2014-12-9 作者:剧中人

自从web进入了2.0的时代,文件上传的功能几乎是遍地开花。小到设置头像,大到网盘管理,都离不开文件上传的功能。今天小剧就来扒一扒和文件上传相关的事儿(本文不讨论swf的上传方式)。

裸奔时代

这里提到的是最原始,也是最基础的上传方式,form提交。只需指定methodactionenctype,再配合一个文件域(inputtype=“file”)即可完成上传。

<form method="post" action="/ajax/upload" enctype="multipart/form-data>
    <input name="file" type="file">
    <input type="submit">
</form>

显而易见,这种上传方式是需要将文件提交到另一个接收上传的页面里。对的,你没有听错,是接收上传的页面,而不是上传接口。所有的接下来的流程必须在接收上传的页面上完成(或者有更精妙的处理方式,这里暂不讨论)。文件上传对于流程的打断其实是蛮严重的。

石器时代

聪明能干的前端工程师们为了解决页面跳转问题,找到了一个较为人性化的方式。因为form提交有一个属性,叫target。可用的属性值和a标签一样,也可以接受自定义名称。利用这个属性,即可完成form表单向特定的页面或iframe进行提交。通过监听iframe的返回值来判断上传结果,而不影响当前页面流程。

<iframe id="uploadA" name="uploadA" src="about:blank"></iframe>
<form method="post" action="/ajax/upload" enctype="multipart/form-data" target="uploadA">
    <input name="file" type="file">
    <input type="submit">
</form>

青铜器时代

上传问题解决,这时候对美的追求也开始了。就像大伙儿知道的一样,文件域在各个浏览器下的外观其实是千差万别的。于是乎在上一时代的基础上,开始了艺术创作。既然需要统一上传按钮的外观,文件域又很难驯服,那就只能狠心的让文件域隐藏不可见,但又可以被用户点击。

<div class="uploadBtn">
    <span>上传</span>
    <iframe id="uploadA" name="uploadA" src="about:blank"></iframe>
    <form method="post" action="/ajax/upload" enctype="multipart/form-data" target="uploadA">
        <input name="file" type="file" />
    </form>
</div>
.uploadBtn{
    position: relative;
    width: 100px;
    height: 26px;
    text-align: center;
    line-height: 26px;
    background: #f70;
    overflow: hidden;
}
.uploadBtn span{
    color: #fff;
}
.uploadBtn input{
    position: absolute;
    top: 0;
    right: 0;
    width: 200%;
    height: 100%;
}
#uploadA{
    position: absolute;
    left: -10px;
    display: block;
    width: 0;
    height: 0;
    border-width: 0;
}
var iframe = $('#uploadA')[0];
//文件变动,自动上传
$('.uploadBtn input').on('change',function(){
    $(this).parents('form').submit();
});
//监听iframe onload事件
if (iframe.attachEvent){
    iframe.attachEvent("onload", function(){
        //iframe.contentDocument
    });
}else{
    iframe.onload = function(){
        //iframe.contentDocument
    };
}

通过这样一番改造,在cssjavascript的精心配合下,用户完全看不见了文件域的踪影,却又在不知不觉中使用着,由最传统的上传方式改造后的功能。

这里简单提下文件域的处理

外观就不说了,直接上交互。IE下,点击浏览按钮即可弹出系统文件选择器,而左侧大部分的区域需要双击才能生效,chrome下则是点击文件域下任意区域即可,firefox部分版本点击左侧无效。 文件域在不同浏览器下的差异

既然左侧区域这么不靠谱,直接用右侧作为点击区域好了,这就是上面的css使用靠右定位input的原因。

这样子就足够了么?显然不。因为小剧前期参与的产品中,上传按钮区域较小,问题并未暴露出来,这里说下问题。

还是IE,因为IE右侧的按钮宽度其实是个相对固定的大小,当上传按钮(给用户看的)宽度大于input的浏览按钮后,同样会出现双击才能选择文件的bug(我说我用过mousemove来解决这个问题你会信我)。

火炮时代

以上的解决是大多数web非插件的上传方式。经过小剧实践,又找到一种较为猥琐的触发方式。

相信很多小伙伴都用过jQuery的trigger方法,用来主动触发一些事件还是很方便的。但是浏览器出于对用户的保护,很多高级事件是不会被响应的。比如移动端对输入框主动触发focus不会弹起软键盘,对文件域主动触发click不会弹出文件选择,等等。

偶然的一次手贱,被我发现,只要是用户主动click页面任何一个位置,与此同时(必须在原生事件的回调中),主动触发文件域的click,是可以调起系统的文件选择器的。

有了这一里应外合的同伙,上面费尽心机去勾搭鼠标、冒充按钮的事情都不用去做了。


PS:目前的上传插件多数为swf,少部分为swf与html5配合使用。本文仅作为抛砖引玉,让小伙伴们了解下原生上传需要注意的几个关键点。另外,本人不自量力造过一个轮子uploader,有兴趣可以去看看。