兼容:ie6/7/8, firefox 3.5.5, opera 10.01, safari 4.0.3, chrome 3.0


效果预览

文件上传
选择文件 重命名 操作 状态
  
重置 选择文件
重置 选择文件
重置 选择文件

ps:由于需要后台,要测试系统请下载实例测试。 
ps2:在完整实例文件中,还有一个文件属性查看实例。

程序说明

【upload】

程序中最重要的方法就是upload了,调用它就可以进行无刷新上传。
upload的过程是这样的,首先用stop方法停止上一次上传,并判断是否选择文件。
然后分别调用_setIframe,_setForm和_setInput,生成需要的iframe,form和input。

如果设置了timeout属性的话,会自动设置计时器:

if(this.timeout>0) {this._timer=setTimeout( $$F.bind(this._timeout,this),this.timeout*1000);
}

ps:经测试,小于0的延时时间,ie会取消执行,而其他浏览器会当成0执行。

程序有一个_sending属性用来判断上传状态。
在stop(停止),dispose(销毁),_finis(完成),_timeout(超时)时会把它设为false。
而在上传开始前要把它设置为true。

最后提交表单就开始上传了。

【iframe】

程序使用_setIframe函数来创建无刷新需要的iframe。

由于ie中iframe的name不能修改的问题,要这样创建iframe:


variframename="QUICKUPLOAD_"+QuickUpload._counter++,
    iframe
=document.createElement( $$B.ie?"<iframe name=\""+ iframename +"\">":"iframe");
iframe.name
=iframename;
iframe.style.display
="none";

ps:关于iframe的name的问题参考这里的iframe部分。
ie8已经可以修改name了,但在非标准(怪辟)模式下还是不能修改。
其中使用了一个QuickUpload函数自身的_counter属性做计算器,这就能保证各个实例的iframe的name就不会重复。

为了能在文件上传完成后执行回调函数,会在iframe的onload中执行_finish函数:


varfinish=this._fFINISH=$$F.bind(this._finish,this);if( $$B.ie ) {
    iframe.attachEvent(
"onload", finish );
}
else{
    iframe.onload
=$$B.opera?function(){this.onload=finish; } : finish;
}

在ie需要用attachEvent来绑定onload,因为在ie中直接设置onload是无效的。
除了用attachEvent还可以用onreadystatechange代替。
至于原因我也不清楚,详细参考“判断 iframe 是否加载完成的完美方法”。

iframe的加载还有一个问题,测试以下代码:


<body><div id="msg">状态:</div></body><script>varmsg=document.getElementById("msg");variframe=document.createElement("iframe");
iframe.onload
=function(){ msg.innerHTML+="onload,"; }
document.body.appendChild(iframe);
iframe.src
="http://cloudgamer.cnblogs.com/"</script>

结果safari, chrome都会触发onload两次,而opera, ff和ie(请自行兼容)都是1次。

估计在safari和chrome在appendChild之后就进行第一次加载,并且在设置src之前加载完毕,所以触发了两次。
如果在插入body之前给iframe随便设置一个src(除了空值),间接加长第一次加载,那么也只触发一次了。
ps:不设置或空值的src相当于链接到“about:blank”(空白页)。

那么opera, ff和ie可能是第一次加载太慢,第二次覆盖了第一次的,所以只触发了一次onload。
ps:也可能是其他原因,例如浏览器优化之类的,我也不确定。

针对加载过快的问题,可以在onload的时候根据_sending确定之前是否上传状态来解决。
虽然没测试出来,会不会有_sending设置之后submit之前刚好触发第一次onload的情况呢?
针对这个问题,在upload方法中会把_sending放在submit之后设置。
那如果在submit之后_sending设置之前就触发了onload呢?(...囧)
这个情况基本不会出现,如果真的出现,就把_sending设置放到submit前面吧。

opera还有一个麻烦的问题,测试下面代码:


<body><div id="msg">状态:</div><form action="http://cloudgamer.cnblogs.com/"target="ifr"></form></body><script>varmsg=document.getElementById("msg");variframe=document.createElement("iframe");
iframe.name
="ifr";
iframe.onload
=function(){ msg.innerHTML+="onload,"; }
document.body.appendChild(iframe);
msg.innerHTML
+="submit,";
document.forms[
0].submit();</script>

ie和ff显示submit,onload,safari和chrome显示的是onload,submit,onload,跟上面的分析一致。
而opera却显示submit,onload,onload,两次onload都是在submit之后触发的。
这个情况就不能单纯用_sending来解决了。
是不是submit不能使iframe取消加载呢?
在appendChild之前设一个src,结果正常的只触发onload一次,看来是可以的啊。

虽然不知道原因,办法还是有的,一个是appendChild前设一个src,还可以在第一次onload中重新设置onload,像程序那样。
但这两个方法都存在不确定性,不能完全解决问题,但也找不到更好的方法了。

ff的onload还有一个问题,在出现ERROR_INTERNET_CONNECTION_RESET(文件大小超过服务器限制)之类的服务器错误时,即使加载完成也不会触发onload,暂时找不到解决办法。

iframe有一个缺陷是只能用onload判断加载完成,但没有办法判断是否加载成功。
没有类似XMLHTTP的status的东西,遇上404之类的错误也没办法判别出来。
在使用时要做好这方面的处理,例如说明允许上传文件大小,超时时间,如何处理长时间无响应等。

【form】

程序使用_setForm函数来创建用来提交数据的form。

要实现无刷新上传,要对form进行特殊的处理:

$$.extend(form, {
    target:
this._iframe.name, method:"post", encoding:"multipart/form-data"});

ps:详细看这里的无刷新上传部分。

由于form是手动插入的,为了不影响原来页面布局还要设置一下form样式,使它“隐形”起来:

$$D.setStyle(form, {
    padding:
0, margin:0, border:0,
    backgroundColor:
"transparent", display:"inline"});

还要注意的是,同一个表单控件只能对应一个form。
如果file控件本身已经有一个form的话,必须在提交前移除:

file.form&&$$E.addEvent(file.form,"submit", $$F.bind(this.dispose,this));

dispose方法是用来销毁程序的,包括移除form。
ps:如果提交前submit被覆盖的话要手动执行一次dispose方法。

最后把form插入到dom:

file.parentNode.insertBefore(form, file).appendChild(file);

先把form插入到file控件之前,然后把file插入到form,这样就能保证file在原来的位置上了。

【input】

如果有其他参数要传递,程序会使用_setInput函数来创建传递数据的表单控件。

由于生成的form里面只有file控件,要传递其他参数只能用程序生成了。
程序用一个_inputs集合来保存当前在form中生成的表单控件。

首先根据自定义的parameter属性创建表单控件:

for( nameinthis.parameter ) {varinput=form[name];if(!input ) {
        input
=document.createElement("input");
        input.name
=name; input.type="hidden";
        form.appendChild(input);
    }
    input.value
=this.parameter[name];
    newInputs[name]
=input;deleteoldInputs[name];
}

当form中没有对应name的控件时,会自动生成一个hidden控件插入到form中。
其中newInputs是用来记录当前生成的控件的,而oldInputs就是_inputs集合。
当设置过对应name的控件后,就从oldInputs中删除对应控件的关联。

然后移除oldInputs关联的控件:

for( nameinoldInputs ) { form.removeChild( oldInputs[name] ); }

这样就能移除上一次生成的无用的控件了。

最后重新记录当前控件到_inputs方便下次使用。

【stop】

如果想停止当前上传操作,可以调用stop方法。

一般来说当iframe发生重载时,会取消上一次的载入,那么只要重新设置src就能取消上传了。
测试以下代码:

<body><iframe id="ifr"name="ifr"></iframe><form action="http://cloudgamer.cnblogs.com/"target="ifr"></form></body><script>document.forms[0].submit();
document.getElementById(
"ifr").src="";</script>

结果都能取消加载,除了opera,未知什么原因。
有两个方法解决,一个是通过form随便用一个action提交一次,还有就是直接移除iframe。
后一个方法比较方便,程序中用_removeIframe方法直接移除iframe。
ps:有更好方法的话记得告诉我。

【dispose】

当使用结束或其他原因要销毁程序时,可以调用dispose方法。

dispose里面主要做的是移除iframe和form。
移除iframe用的是_removeIframe方法,首先把onload移除,再把iframe从body移除:

variframe=this._iframe;
$$B.ie
?iframe.detachEvent("onload",this._fFINISH ) : ( iframe.onload=null);
document.body.removeChild(iframe);
this._iframe=null;

十分简单,但在ff有一个问题,测试以下代码:

<form target="ifr"action="x"><input id="btn"type="submit"value="click"></form><iframe name="ifr"id="ifr"></iframe><script>document.getElementById("btn").onclick=function(){
    document.getElementById(
"ifr").onload=function(){this.parentNode.removeChild(this);
    };
}
</script>

提交后都能移除iframe,但ff还一直显示“载入中”的状态。
不过解决方法也很简单,用setTimeout设置一个延时,让iframe执行完整就可以了。
所以在dispose中是这样调用_removeIframe的:

if( $$B.firefox ) {
    setTimeout($$F.bind(
this._removeIframe,this),0);
}
else{this._removeIframe();
}

至于form的移除就比较简单,在_removeForm这样处理:

var form=this._form, parent=form.parentNode;if( parent ) {
    parent.insertBefore(
this.file, form); parent.removeChild(form);
}
this._form=this._inputs=null;

要判断一下parentNode,否则如果parentNode不存在的话后面的会执行出错。

【file的reset】

在实例里,有一个用来重置file控件的ResetFile函数。

重置file控件一般的办法是使所在的form执行reset,但问题是会把其他表单控件也重置了。
以前由于安全问题,file的value是不允许修改的。
但现在ff,chrome和safari可以把它设为空值来实现重置:

file.value="";

当然其他值还是不允许的。
ps:记忆中以前是不行的,不知有没有记错。

对于opera,有一个变通的方法,利用它的type属性:

file.type="text"; file.type="file";

通过修改type得到的file控件,value会自动还原成空值,这样就间接把file控件清空了。
ps:利用这个方法可以间接得到文件路径,但由于变回去后值就清空了,所以没什么用。

而ie的表单控件的type设置后是不允许修改的,不能用opera的办法。
不过还是有以下方法解决:
1,新建一个form,把file插进入后reset,再移除:

with(file.parentNode.insertBefore(document.createElement('form'), file)){
    appendChild(file); reset(); removeNode(
false);
}

好处是使用原生的reset,稳定可靠,但效率低。
ps:removeNode只有ie和opera支持,如需兼容可改用removeChild的方式。

2,利用outerHTML,重建一个file控件:

file.outerHTML=file.outerHTML;

好处是高效,但由于是新创建的file控件,之前关联的东西都丢失了。
ps:ff支持不支持outerHTML。

3,利用cloneNode,复制一个file控件:

file.parentNode.replaceChild(file.cloneNode(false), file);

跟上一个方法差不多,但效率更低。

4,利用select方法选中file控件的文本域,再进行清空:

file.select(); document.selection.clear();

file.select(); document.selection.clear();

看来没什么问题,但file必须能被select(不能是隐藏状态)。
ps:这两个方法都只能在ie使用。

由于程序中file是需要关联的,所以方法2和3都不能用。
方法4貌似也不错,但有一个致命问题,在ie测试以下代码:

<form><input id="test"name="file"type="file"></form><script>document.getElementById("test").onchange=function(){this.select(); document.selection.clear();this.form.submit();
}
</script>

执行到submit会显示“拒绝访问”的错误,原因不清楚,不知是ie故意的还是bug。

看来也只能使用方法1了:

functionResetFile(file){
    file.value
="";//ff chrome safariif( file.value ) {if( $$B.ie ) {//iewith(file.parentNode.insertBefore(document.createElement('form'), file)){
                appendChild(file); reset(); removeNode(
false);
            }
        }
else{//operafile.type="text"; file.type="file";
        }
    }
}

ps:有更好方法的话记得告诉我啊。

这个函数并不够通用,最好还是根据实际情况选择需要的方法。

使用技巧

【上传文件数】

在文件上传实例中,各个文件是同时上传的。
经测试,浏览器能同时上传的文件数如下:
ie 2
ff 8
opera 8
chrome 6
safari 6
由于ie最多同时只能传2个,所以设置更多文件也只能排队,而不能达到同时上传的效果的。
ps:只是目测结果,有错请提出。

【传递参数】

上传文件实例中,可以传递对应的修改文件名,在使用“一般上传”多个文件一起上传时也能找到对应的文件名。
因为表单控件值传递到后台后,获取数据的顺序跟前台表单控件的排列顺序是一致的。
只要保证前台file控件跟对应表单控件的排列顺序一致就能利用这个特性获取对应的值了。
详细参考后台代码。

【回调函数】

有两个方法可以响应上传完成回调函数。
一种是后台上传完成后,在iframe输出并执行回调函数或通过parent调用父窗口的回调函数。
这种比较方便,但必须在iframe里面执行处理,例如文件属性查看实例。
另一种是在iframe的onload中执行回调函数。
好处是可以把所有处理放在父窗口,iframe可以不做任何处理或用来反馈信息。
缺点是有兼容性问题,而且会有加载后没有触发onload的情况(上面的iframe部分有说明)。
上传文件实例中就是在onFinish中处理在iframe中输出的数据。
由于可能出现一些意外情况导致响应很久,甚至没有响应,所以一定要设置timeout以防万一。

【处理返回数据】

上面提到,可以在onFinish中处理在iframe中输出的数据。
要从iframe的body中获取数据,有以下几个方法:
iframe.contentWindow.document.body.innerHTML
iframe.contentDocument.body.innerHTML
window.frames[iframename].document.body.innerHTML
其中前两种差不多,后者比较简便,但ie不支持contentDocument,可惜。
第三种是利用frames对象来获取,注意这样获取的对象直接就是window对象。
由于程序能直接获得iframe对象,所以用的是第一种方式。
不过有一个问题在iframe的部分也提过,就是返回错误信息页面的问题。
在上传文件实例中,在iframe中输出的是json形式的文件信息数据。
在onFinish中是这样处理的:

try{varinfo=eval("("+iframe.contentWindow.document.body.innerHTML+")");
    show(
"上传完成");
}
catch(e){
    show(
"上传失败"); stop();return;
}

只有返回正确的json格式数据才能正常运行,否则就抛出错误,间接地排除了404等错误信息。
ps:有更好方法的话欢迎提出。

【销毁程序】

程序中有不少dom操作,在不需要继续使用的时候最好执行一次dispose方法来销毁程序。
例如移除file之后,关闭窗口之前,提交表单之前,历遍表单元素前等等。
既可以节省资源,防止dom的内存泄漏,又能避免表单嵌套时的冲突问题。

【可用性】

看过“ppk谈javascript”后,更加注重了可用性。
上传实例在浏览器不支持js的情况下也能正常上传,各位可以自行测试。

【编码】

上一个无刷新上传系统,很多人反映上传后文件名乱码,后来发现是编码的问题。
当有中文信息传递时,要注意前后台的编码必须统一,包括charset,文件编码,web.config的配置等。

【asp版本】

asp版本跟.net版本功能是一样的,使用无组件上传类。
不过上传类本身有一个缺陷导致提交同名file控件的话会出错,经过修改后现在可以正常使用了。

使用说明

实例化时,第一个必要参数是file控件对象:

newQuickUpload(file);

第二个可选参数用来设置系统的默认属性,包括
属性:    默认值//说明
parameter: {},//参数对象
action:  "",//设置action
timeout: 0,//设置超时(秒为单位)
onReady: function(){},//上传准备时执行
onFinish: function(){},//上传完成时执行
onStop:  function(){},//上传停止时执行
onTimeout: function(){}//上传超时时执行

还提供了以下方法:
upload:执行上传操作;
stop:停止上传操作;
dispose:销毁程序。

程序源码

varQuickUpload=function(file, options) {this.file=$$(file);this._sending=false;//是否正在上传this._timer=null;//定时器this._iframe=null;//iframe对象this._form=null;//form对象this._inputs={};//input对象this._fFINISH=null;//完成执行函数$$.extend(this,this._setOptions(options));
};
QuickUpload._counter
=1;
QuickUpload.prototype
={//设置默认属性_setOptions:function(options) {this.options={//默认值action:"",//设置actiontimeout:0,//设置超时(秒为单位)parameter:    {},//参数对象onReady:function(){},//上传准备时执行onFinish:function(){},//上传完成时执行onStop:function(){},//上传停止时执行onTimeout:function(){}//上传超时时执行};return$$.extend(this.options, options||{});
  },
//上传文件upload:function() {//停止上一次上传this.stop();//没有文件返回if(!this.file||!this.file.value )return;//可能在onReady中修改相关属性所以放前面this.onReady();//设置iframe,form和表单控件this._setIframe();this._setForm();this._setInput();//设置超时if(this.timeout>0) {this._timer=setTimeout( $$F.bind(this._timeout,this),this.timeout*1000);
    }
//开始上传this._form.submit();this._sending=true;
  },
//设置iframe_setIframe:function() {if(!this._iframe ) {//创建iframevariframename="QUICKUPLOAD_"+QuickUpload._counter++,
            iframe
=document.createElement( $$B.ie?"<iframe name=\""+ iframename +"\">":"iframe");
        iframe.name
=iframename;
        iframe.style.display
="none";//记录完成程序方便移除varfinish=this._fFINISH=$$F.bind(this._finish,this);//iframe加载完后执行完成程序if( $$B.ie ) {
            iframe.attachEvent(
"onload", finish );
        }
else{
            iframe.onload
=$$B.opera?function(){this.onload=finish; } : finish;
        }
//插入bodyvarbody=document.body; body.insertBefore( iframe, body.childNodes[0] );this._iframe=iframe;
    }
  },
//设置form_setForm:function() {if(!this._form ) {varform=document.createElement('form'), file=this.file;//设置属性$$.extend(form, {
            target:
this._iframe.name, method:"post", encoding:"multipart/form-data"});//设置样式$$D.setStyle(form, {
            padding:
0, margin:0, border:0,
            backgroundColor:
"transparent", display:"inline"});//提交前去掉formfile.form&&$$E.addEvent(file.form,"submit", $$F.bind(this.dispose,this));//插入formfile.parentNode.insertBefore(form, file).appendChild(file);this._form=form;
    }
//action可能会修改this._form.action=this.action;
  },
//设置input_setInput:function() {varform=this._form, oldInputs=this._inputs, newInputs={}, name;//设置inputfor( nameinthis.parameter ) {varinput=form[name];if(!input ) {//如果没有对应input新建一个input=document.createElement("input");
            input.name
=name; input.type="hidden";
            form.appendChild(input);
        }
        input.value
=this.parameter[name];//记录当前inputnewInputs[name]=input;//删除已有记录deleteoldInputs[name];
    }
//移除无用inputfor( nameinoldInputs ) { form.removeChild( oldInputs[name] ); }//保存当前inputthis._inputs=newInputs;
  },
//停止上传stop:function() {if(this._sending ) {this._sending=false;
        clearTimeout(
this._timer);//重置iframeif( $$B.opera ) {//opera通过设置src会有问题this._removeIframe();
        }
else{this._iframe.src="";
        }
this.onStop();
    }
  },
//销毁程序dispose:function() {this._sending=false;
    clearTimeout(
this._timer);//清除iframeif( $$B.firefox ) {
        setTimeout($$F.bind(
this._removeIframe,this),0);
    }
else{this._removeIframe();
    }
//清除formthis._removeForm();//清除dom关联this._inputs=this._fFINISH=this.file=null;
  },
//清除iframe_removeIframe:function() {if(this._iframe ) {variframe=this._iframe;
        $$B.ie
?iframe.detachEvent("onload",this._fFINISH ) : ( iframe.onload=null);
        document.body.removeChild(iframe);
this._iframe=null;
    }
  },
//清除form_removeForm:function() {if(this._form ) {varform=this._form, parent=form.parentNode;if( parent ) {
            parent.insertBefore(
this.file, form); parent.removeChild(form);
        }
this._form=this._inputs=null;
    }
  },
//超时函数_timeout:function() {if(this._sending ) {this._sending=false;this.stop();this.onTimeout(); }
  },
//完成函数_finish:function() {if(this._sending ) {this._sending=false;this.onFinish(this._iframe); }
  }
}

完整实例下载 
完整实例下载(asp版本)

转载于:https://www.cnblogs.com/top5/archive/2011/06/17/2083536.html

简便无刷新文件上传系统相关推荐

  1. [转]仿163网盘无刷新文件上传系统

    原文链接:http://www.cnblogs.com/cloudgamer/archive/2008/10/20/1314766.html 这个仿163网盘无刷新文件上传系统,并没有用使用.net的 ...

  2. 在本地测试无组件上传类上传大文件可以,在服务器上就不行,仿163网盘无刷新文件上传系统...

    回复  引用  查看     2008-10-20 11:03 | fkeuem 真的很不错.谢谢. 回复  引用  查看     2008-10-20 11:20 | PuserChen 下载了,学 ...

  3. 实用ExtJS教程100例-009:ExtJS Form无刷新文件上传

    文件上传在Web程序开发中必不可少,ExtJS Form中有一个filefield字段,用来选择文件并上传.今天我们来演示一下如何通过filefield实现ExtJS Form无刷新的文件上传. 首先 ...

  4. Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)

    相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦苦来实现呢?我并不否认"拿来主义",只是我个人 ...

  5. Javascrpt无刷新文件上传

    最近工作中遇到上传文件问题,主要需求是一步点击上传,兼容ie8+,当时用的dojox/form/uploader控件,这两天扒了一下源码,明白了原理拿出来分享一下. 总体思路如下: 1.对于支持XML ...

  6. jquert ajax文件 mvc,jquery ajax file upload NET MVC 无刷新文件上传

    //MVC实现 public classFileController : Controller { [HttpPost]publicActionResult Setting() {string use ...

  7. 仿163网盘无刷新多文件上传系统

    这个仿163网盘无刷新多文件上传系统,并没有用使用.net的控件,完全的手工制作.前台基本上是静态的,跟后台没有关系,所以后台用什么语言做都可以(后面有各个版本的实例下载). 本来觉得这个系统会很复杂 ...

  8. html5拖拽图片批量ajax无刷新进度上传

    1.前端拖拽图片 之前有篇文章说到HTML5的拖拽(drag.drop,详见:/post/jquery-plugin-1-jquery-drag-and-html5-draggable-api-and ...

  9. 使用jspsmartupload完成简单的文件上传系统

    请不要妄想,一个html的file控件,再加上JavaScript与jQuery语句就可以完成文件上传, 文件上传系统从来是需要配合服务器来完成的 用户把自己的文件上传到服务器上 文件上传系统是很复杂 ...

最新文章

  1. ---Mybatis3学习笔记(2)
  2. 走近NAP功能 全面了解Vista系统安全机制
  3. okhttp 工具类_日语学习工具推荐,小白必备!
  4. java排班_使用java规则引擎Drools自动排班前言.doc
  5. WordPress好看的QQ微信等登录插件二开美化版
  6. C++之函数返回数组
  7. [Java] 蓝桥杯ALGO-103 算法训练 完数
  8. 机器学习/深度学习测试题(一) —— 单层感知器的激活函数
  9. ecshop mysql 标题表_ecshop商品分类列表页如何自定义title标题
  10. 生怕认可java+flatmap,RxJava 好难理解的一个 FlatMap 的例子
  11. rabbitmq - (消息队列) 的基本原理介绍
  12. 考研高数 专题11:多元复合函数及隐函数求导的方法和技巧【灵活】
  13. Twitter与微博
  14. StringBuilder和输入输出
  15. 项目管理工具——项目开发者工具
  16. SSL证书的加密算法都有哪些?
  17. 【记vue项目中的踩坑日记】一杯茶一包烟,一个bug搞一天
  18. 最常见的Git错误都有哪些,如何解决它们?
  19. 获得研究生学历的6种方式
  20. MODBUS功能码解释

热门文章

  1. javaScript的常见document对象
  2. 最耗性能的SQL语句
  3. django03_表单(forms.ModelForm)(login前后台)
  4. MFC和Direct3D9一起使用
  5. PHP Warning: date(): It is not safe to rely on the system’s timezone settings
  6. PAT1011. A+B和C
  7. php找不到库,64位系统下编译PHP找不到库文件问题 | 学步园
  8. Python中的问卷调查(华为机测题)
  9. 2017小米面试题(句子反转)
  10. python的随机种子实例