无刷新页面文件上传

  文件上传很常见,也有好些方法,不过我见过的大多是基于flash的,也有用XMLhttpResponse来提交文件的,其实大可不必这么麻烦,我这里介绍一种更具兼容性的用法,利用iframe来曲线实现无刷新页面文件上传功能,这种方法其实也很常见了。

  iframe上传文件原理是这样的:首先input file控件本身是被表单包含的,表单属性必须设置为enctype="multipart/form-data",才能上传任意文件。但是仅仅有这个还不行, 因为提交表单后,它会自动刷新页面,为了不让它离开本页,我们可以在这个提交的页面上动态生成一个隐藏的iframe,但是别忘了在表单上再添加一个属性target,并且指定为iframe的name,这样表单提交后,服务器才会将返回的结果会写在iframe里。最后在从这个iframe里获取数据就可以了。

  原理上不难理解,也不难实现。在这上面还可以扩展出更多的功能,比如上传前判断文件类型,大小之类的,添加一个加载的过程等等。不过在实现的过程,还是遇到了几个难点:一个是获取文件信息,这个在不同的浏览器上,获取的方法不尽相同;另外一个是获取iframe里的内容。这些问题的解决方法,我没有很好的解决,不过貌似也没有什么大问题,所以我把解决的方法写在这里。

获取文件信息:我这里只是实现了获取文件名称的功能。

 1 getFile: function() {
2 var fileInfo = {};
3 var isIE = !!window.ActiveXObject;
4 // var isIE6 = isIE&&!window.XMLHttpRequest;
5 // var isIE8 = isIE&&!!document.documentMode;
6 // var isIE7 = isIE&&!isIE6&&!isIE8;
7 var path = '';
8 if(isIE) {
9 path = document.getElementById(this.config.id).value;
10 fileInfo.fileName = path.slice(path.lastIndexOf('\\')+1);
11 } else fileInfo.fileName = document.getElementById(this.config.id).files[0].name;
12 return fileInfo;
13 }

获取iframe的内容:

document.getElementById("iframe").find('body').html();

  我这里先展示一个基本的上传代码,供各位参考。 我这个是不带界面的,纯逻辑部分。页面须有个<input type='file' id='uploader' name='file'/>标签,然后可以这样调用: 

var uploader = new FileUploader({
action: 'url', // 上传地址
onChange: function() {}, // 上传前
loading: function() {}, // 上传过程中
onComplete: function() {} //上传完成后
}) ;

点击按钮选择一个文件后就会立即提交。 

  完整代码:

 1 function FileUploader(config) {
2 this.config = config;
3 this.input = $('#'+this.config.id);
4 this.action = this.config.action || '';
5 this.method = this.config.method || 'post';
6 this.type = this.config.type || 'json';
7 this.init();
8 }
9 FileUploader.prototype = {
10 init: function() {
11 var that = this;
12 this.addListener('change', function() {
13 if(that.config.onChange) {
14 var res = false;
15 res = that.config.onChange.call(this, that.getFile());
16 if(!res) return false;
17 }
18 that.load();
19 });
20 },
21 load: function() {
22 var that = this;
23 this.wrap();
24 this.send();
25 if(that.config.loading) that.config.loading.call(that);
26 $('iframe').bind('load', function() {
27 var data = that.getResponse(document.getElementById("hidden_frame")).find('body').html();
28 data = data.replace(/<pre.*>(.*)<\/pre>/, "$1");
29 if(that.config.onComplete) {
30 if(that.type == 'json') data = eval('('+data+')');
31 that.config.onComplete.call(this, data, that.input);
32 }
33 // this.input.unwrap();
34 $(this).remove();
35 });
36 },
37 getResponse: function(iframe) {
38 var doc = $(iframe).contents();
39 return doc;
40 },
41 remove: function() {
42 this.input.remove();
43 },
44 getFile: function() {
45 var fileInfo = {};
46 var isIE = !!window.ActiveXObject;
47 var isIE6 = isIE&&!window.XMLHttpRequest;
48 var isIE8 = isIE&&!!document.documentMode;
49 var isIE7 = isIE&&!isIE6&&!isIE8;
50 var path = '';
51 if(isIE) {
52 path = document.getElementById(this.config.id).value;
53 fileInfo.fileName = path.slice(path.lastIndexOf('\\')+1);
54 } else fileInfo.fileName = document.getElementById(this.config.id).files[0].name;
55 return fileInfo;
56 },
57 send: function(cb) {
58 var that = this;
59 this.input.parent('form').submit();
60 },
61 wrap: function() {
62 this.input.wrap(
63 '<form enctype="multipart/form-data"'+
64 'action="'+this.action+'" method="'+this.method+'" target="hidden_frame">'+
65 '</form>'
66 );
67 this.input.parent('form').after(
68 '<iframe name="hidden_frame" id="hidden_frame" src="javascript:false;" style="display:none"></iframe>'
69 );
70 },
71 addListener: function(type, cb) {
72 if(type == 'change') this.input.bind('change', cb);
73 }
74 };


注: 1. 应将loading()方法应写到send()的回调中,主要是考虑到加载过程中,有中途放弃上传的可能性;

   2. form的method方法必须设为post。

 

   

posted @ 2012-03-22 18:03  bilipan  阅读(571)  评论(0编辑  收藏  举报