页面里经常要用到文件上传的功能,而且要求页面不刷新,先说一下原理:页面里放一个file控件和submit按钮,外面用form表单包住,给form表单加上对应的属性值,action、method、entype、name,到这一步,能上传文件了,但是这样上传文件会刷新页面,这不是我们想要的。我们要的是文件上传时不刷新页面,那么也简单,在页面里放一个iframe,设置它的宽高为0,这里有两个坑:
1、需要设置iframe的name值与form的target属性值一样,意思就是把form表单上传文件的刷新转嫁到iframe里去了;
2、form表单的enctype属性值必须设置成multipart/form-data,将文件转换成文件流供后端接收;
代码如下:
页面(这里为了看到效果,就不将iframe的宽高设为0了):
事情就这么愉快地结束了吗?当然没有,离国庆节还有那么些天,不要着急。
到这里文件能上传了,页面也不会刷新,那么还差什么?当然是精益求精--优化啦。怎么优化?假如页面里有三个地方需要上传不同类型的文件,最好的办法肯定不是在页面里将代码copy三份,然后就这样用,这是普通开发的做法,我们可以利用js动态生成上面这些代码,需要上传文件的地方,一个函数加参数就搞定了,代码如下:
文件要提交到的地址;
opt.fileName : file控件的name;
opt.format : 文件格式,以数组的形式传递,如['jpg','png','gif','bmp'];
opt.callBack : 上传成功后回调;
iName=opt.frameName;
iframe = $(''= $(''= $(''
文件
文件后,验证文件格式是否符合要求
file.change(文件的扩展名
fileFormat=$().val().exec(/\.[a-zA-Z]+$/)[0].substring(1(opt.format.join('-').indexOf(fileVal)!=-1
}'文件格式错误,请重新选择!'文件提交完后
iframe.load( data = $().contents().find('body'
使用方法:在页面里放一个按钮Btn,点击Btn时触发ajaxUpload方法,ajaxUpload方法内部自动创建上传所需要的元素并自动触发file.click()事件供用户选择文件,选中文件后自动验证文件格式并提交,然后返回后端返回的结果,到这里,问题解决了80%,为什么不是100%?ajaxUpload方法在IE8以上及火狐、chrome浏览器都没有问题,但在IE8及以下的浏览器上传文件会提示:低版本的IE做了安全限制,file控件必须由用户主动点击触发选择的文件才可以上传,而不能使用js的click事件来模拟点击触发。在此我又想说,IE我~!@#¥%……&*()——……。
用户点击来触发,那么直接把页面里的按钮替换成file控件吧,iframe和form还是动态创建,当用户点击file控件选择文件后,会触发file控件的chang事件,给file控件的change事件绑定ajaxUpload方法并将file控件的id传进去,ajaxUpload方法通过id获取file控件并将file控件appendTo到动态创建的form里,之后的步骤与上面无异——验证格式→提交表单→触发回调。细心的同学会发现,选择文件后,file控件会appendTo到form表单里,那页面里放file控件的地方不是空了么?并且,表单提交后,file会随着form一起被remove掉,所以,在file控件appendTo到form前,先建一个变量P将file控件的父级存起来,form表单提交之后,先将file控件appendTo回到P页面里消失,所以页面里的Btn要保留,把file控件定位在Btn上面,透明度设置成0,这样点击Btn实际上点击的是盖在上面的file控件,这样即使file控件被appendTo到form里面,用户也不会察觉到什么变化,问题迎刃而解!兼容的写法如下:
function页面里file控件的ID;
opt.frameName : iframe的name值;
opt.url : 文件要提交到的地址;
opt.format : 文件格式,以数组的形式传递,如['jpg',form,file,fileParent;
iframe = $(''= $(''= $('#'+opt.id); 获取flie控件
fileParent = file.parent();
};
</span><span style="color: #008000;">//</span><span style="color: #008000;">取得所选<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>的扩展名</span>
<span style="color: #0000ff;">var</span> fileFormat=/\.[a-zA-Z]+$/.exec(file.val())[0].substring(1<span style="color: #000000;">);
</span><span style="color: #0000ff;">if</span>(opt.format.join('-').indexOf(fileFormat)!=-1<span style="color: #000000;">){
form.submit();</span><span style="color: #008000;">//</span><span style="color: #008000;">格式通过验证后提交表单;</span>
}<span style="color: #0000ff;">else</span><span style="color: #000000;">{
file.appendTo(fileParent); </span><span style="color: #008000;">//</span><span style="color: #008000;">将file控件放回到<a href="/tag/yemian/" target="_blank" class="keywords">页面</a></span>
<span style="color: #000000;"> iframe.remove();
form.remove();
alert(
};
</span><span style="color: #008000;">//</span><span style="color: #008000;"><a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>提交完后</span>
iframe.load(<span style="color: #0000ff;">function</span><span style="color: #000000;">(){
</span><span style="color: #0000ff;">var</span> data = $(<span style="color: #0000ff;">this</span>).contents().find('body'<span style="color: #000000;">).html();
file.appendTo(fileParent);
iframe.remove();
form.remove();
opt.callBack(data);
})
}
方法已经接近完美了,为什么是接近?来看一张图片:

结构代码:
选择文件
这就是放在页面里的file控件,外面用一个div包住,在IE10及以下浏览器中,用户单击红色框部分是不会弹出文件选择框的,必须单击蓝色部分或双击红色部分才行,要让蓝色部分占满外面的div怎么做到呢?用css设置宽度只会增加红色部分的宽度,这时我们会发现蓝色部分是有字的,对,可以通过设置font-size来使蓝色部分变宽,然后给file控件加上dir="rtl",这会让浏览按钮移到左边,再给.btn加上overflow:hidden,可以发现浏览按钮已经占满整个div了,如下图:

到这里才真正完成了100%,最后,我们还可以给file控件设置accept属性,限制可选文件格式(IE8及以下不支持该属性),别忘了把file控件的透明的改为0。