自从web进入了2.0的时代,文件上传的功能几乎是遍地开花。小到设置头像,大到网盘管理,都离不开文件上传的功能。今天小剧就来扒一扒和文件上传相关的事儿(本文不讨论swf的上传方式)。
这里提到的是最原始,也是最基础的上传方式,form提交。只需指定method
、action
、enctype
,再配合一个文件域(input
,type=“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 }; }
通过这样一番改造,在css
与javascript
的精心配合下,用户完全看不见了文件域的踪影,却又在不知不觉中使用着,由最传统的上传方式改造后的功能。
外观就不说了,直接上交互。IE下,点击浏览按钮即可弹出系统文件选择器,而左侧大部分的区域需要双击才能生效,chrome下则是点击文件域下任意区域即可,firefox部分版本点击左侧无效。
既然左侧区域这么不靠谱,直接用右侧作为点击区域好了,这就是上面的css使用靠右定位input的原因。
这样子就足够了么?显然不。因为小剧前期参与的产品中,上传按钮区域较小,问题并未暴露出来,这里说下问题。
还是IE,因为IE右侧的按钮宽度其实是个相对固定的大小,当上传按钮(给用户看的)宽度大于input的浏览按钮后,同样会出现双击才能选择文件的bug(我说我用过mousemove来解决这个问题你会信我)。
以上的解决是大多数web非插件的上传方式。经过小剧实践,又找到一种较为猥琐的触发方式。
相信很多小伙伴都用过jQuery的trigger方法,用来主动触发一些事件还是很方便的。但是浏览器出于对用户的保护,很多高级事件是不会被响应的。比如移动端对输入框主动触发focus不会弹起软键盘,对文件域主动触发click不会弹出文件选择,等等。
偶然的一次手贱,被我发现,只要是用户主动click页面任何一个位置,与此同时(必须在原生事件的回调中),主动触发文件域的click,是可以调起系统的文件选择器的。
有了这一里应外合的同伙,上面费尽心机去勾搭鼠标、冒充按钮的事情都不用去做了。
PS:目前的上传插件多数为swf,少部分为swf与html5配合使用。本文仅作为抛砖引玉,让小伙伴们了解下原生上传需要注意的几个关键点。