唐山网站建设

设为主页 加入收藏 繁體中文

简便无刷新文件上传系统

核心提示:之前写过1个仿163网盘无刷新多文件上传系统,已对无刷新上传文件的原理做了具体的分析。而这次的系统主要是针对单个file控件的,便携版,使用更简单,还有更深进的分析。

程序说明

【upload】

程序中最重要的方法就是upload了,调用它便可以够进行无刷新上传。upload的进程是这样的,首先用stop方法停止上1次上传,并判定是否是选择文件。然后分别调用_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履行。

程序有1个_sending属性用来判定上传状态。在stop(停止),remove(移除),_finis(完成),_timeout(超时)中会把它设为false。而在上传开始前要把它设置为true。最后提交表单就开始上传了。

【iframe】

程序使用_setIframe函数来创建无刷新需要的iframe。由于ie中iframe的name不能修改的题目,要这样创建iframe:  

以下为援用的内容:
var iframename = "QUICKUPLOAD_" + QuickUpload._counter++,
    iframe 
= document.createElement( $$B.ie ? """ + iframename + "\">" : "iframe");
iframe.name 
= iframename;
iframe.style.display 
= "none";

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

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

以下为援用的内容:
var finish = 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的加载还有1个题目,测试以下代码:

以下为援用的内容:
<body><div id="msg">状态:</div></body>
<script>
var msg = document.getElementById("msg");
var iframe = 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以后就进行第1次加载,并且在设置src之前加载终了,所以触发了两次。假设在插进body之前给iframe随便设置1个src(除空值),间接加长第1次加载,那末也只触发1次了。ps:不设置或空值的src相当于链接到“about:blank”(空缺页)。

那末opera, ff和ie多是第1次加载太慢,第2次覆盖了第1次的,所以只触发了1次onload。ps:也多是其他缘由,例如浏览器优化之类的,我也不肯定。

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

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

以下为援用的内容:
<body>
<div id="msg">状态:</div>
<form action="http://cloudgamer.cnblogs.com/" target="ifr">
</form>
</body>
<script>
var msg = document.getElementById("msg");
var iframe = 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,跟上面的分析1致。而opera却显示submit,onload,onload,两次onload都是在submit以后触发的。这个情况就不能单纯用_sending来解决了。是否是submit不能使iframe取消加载呢?在appendChild之前设1个src,结果正常的只触发onload1次,看来是可以的啊。

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

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

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

1 2 3 4 下1页

核心提示:之前写过1个仿163网盘无刷新多文件上传系统,已对无刷新上传文件的原理做了具体的分析。而这次的系统主要是针对单个file控件的,便携版,使用更简单,还有更深进的分析。

【form】

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

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

以下为援用的内容:
$$.extend(form, {
    target: 
this._iframe.name, method: "post", encoding: "multipart/form-data"
});

ps:具体看这里的无刷新上传部份

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

以下为援用的内容:
$$D.setStyle(form, {
    padding: 
0, margin: 0, border: 0,
    backgroundColor: 
"transparent", display: "inline"
});

还要留意的是,同1个表单控件只能对应1个form。假设file控件本身已有1个form的话,必须在提交前移除:

以下为援用的内容:
file.form && $$E.addEvent(file.form, "submit", $$F.bind(this.remove, this));

remove方法是用来移除程序的,包括移除form。ps:假设提交前submit被覆盖的话要手动履行1次remove程序。

最后把form插进到dom: 

以下为援用的内容:
file.parentNode.insertBefore(form, file).appendChild(file);

先把form插进到file控件之前,然后把file插进到form,这样便可以保证file在原来的位置上了。

【input】

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

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

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

以下为援用的内容:
for ( name in this.parameter ) {
    
var input = 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;
    
delete oldInputs[name];
}

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

然后移除oldInputs关联的控件:

以下为援用的内容:
for ( name in oldInputs ) { form.removeChild( oldInputs[name] ); }

这样便可以移除上1次天生的无用的控件了。最后重新记录当前控件到_inputs方便下次使用。

【stop】

假设想停止当前上传操纵,可以调用stop方法。

1般来讲当iframe产生重载时,会取消上1次的载进,那末只要重新设置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,未知甚么缘由。有两个方法解决,1个是通过form随便用1个action提交1次,还有就是直接移除iframe。后1个方法比较方便,程序中用_removeIframe方法直接移除iframe。ps:有更好方法的话记得告知我。

【remove】

当使用结束或其他缘由要清除程序时,可以调用remove方法。

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

以下为援用的内容:
var iframe = this._iframe;
$$B.ie 
? iframe.detachEvent( "onload"this._fFINISH ) : ( iframe.onload = null );
document.body.removeChild(iframe); 
this._iframe = null;

10分简单,但在ff有1个题目,测试以下代码:

以下为援用的内容:
<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还1直显示“载进中”的状态。不过解决方法也很简单,用setTimeout设置1个延时,让iframe履行完全便可以够了。所以在remove中是这样调用_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;

要判定1下parentNode,否则假设parentNode不存在的话后面的会履行出错。

上1页 1 2 3 4 下1页

核心提示:之前写过1个仿163网盘无刷新多文件上传系统,已对无刷新上传文件的原理做了具体的分析。而这次的系统主要是针对单个file控件的,便携版,使用更简单,还有更深进的分析。

【file的reset】

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

重置file控件1般的办法是使所在的form履行reset,但题目是会把其他表单控件也重置了。之前由于安全题目,file的value是不答应修改的。但现在ff,chrome和safari可以把它设为空值来实现重置:file.value = "";

固然其他值还是不答应的。ps:记忆中之前是不行的,不知有无记错。对opera,有1个变通的方法,利用它的type属性:file.type = "text"; file.type = "file";

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

而ie的表单控件的type设置后是不答应修改的,不能用opera的办法。不过还是有以下方法解决:

1,新建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,重建1个file控件:file.outerHTML = file.outerHTML; 好处是高效,但由因而新创建的file控件,之前关联的东西都丢失了。

3,利用cloneNode,复制1个file控件:file.parentNode.replaceChild(file.cloneNode(false), file);  跟上1个方法差未几,但效率更低。

4,利用select方法选中file控件的文本域,再进行清空:file.select(); document.selection.clear(); 或 file.select(); document.selection.clear();

基本没有题目,但file必须能被select(不能是隐躲状态)。ps:这两个方法都只能在ie使用。

由于程序中file是需要关联的,所以方法2和3都不能用。方法4貌似也不错,但有1个致命题目,在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早就猜到这类“诡计”不答应这样操纵了。

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

以下为援用的内容:
function ResetFile(file){
    file.value 
= "";//ff chrome safari
    if ( file.value ) {
        
if ( $$B.ie ) {//ie
            with(file.parentNode.insertBefore(document.createElement('form'), file)){
                appendChild(file); reset(); removeNode(
false);
            }
        } 
else {//opera
            file.type = "text"; file.type = "file";
        }
    }
}

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

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

上1页 1 2 3 4 下1页

核心提示:之前写过1个仿163网盘无刷新多文件上传系统,已对无刷新上传文件的原理做了具体的分析。而这次的系统主要是针对单个file控件的,便携版,使用更简单,还有更深进的分析。

使用技能

【上传文件数】

在文件上传实例中,各个文件是同时上传的。
经测试,浏览器能同时上传的文件数以下:
ie 2
ff 8
opera 8
chrome 6
safari 6

由于ie最多同时只能传2个,所以设置更多文件也只能排队,而不能到达同时上传的效果的。ps:测试不太专业,可能有错。

【传递参数】

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

【回调函数】

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

【处理返回数据】

上面提到,可以在onFinish中处理在iframe中输出的数据。要从iframe的body中获得数据,有以下几个方法:
iframe.contentWindow.document.body.innerHTML
iframe.contentDocument.body.innerHTML
window.frames[iframename].document.body.innerHTML

其中前两种差未几,后者比较简便,但ie不支持contentDocument,惋惜。第3种是利用frames对象来获得,留意这样获得的对象直接就是window对象。由于程序能直接取得iframe对象,所以用的是第1种方式。不过有1个题目在iframe的部份也提过,就是返回毛病信息页面的题目。在上传文件实例中,在iframe中输出的是json情势的文件信息数据。在onFinish中是这样处理的:

以下为援用的内容:
try{
    
var info = eval("(" + iframe.contentWindow.document.body.innerHTML + ")");
    show(
"上传完成");
}
catch(e){
    show(
"上传失败"); stop(); return;
}

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

【移除程序】

程序中有很多dom操纵,在不需要继续使用的时候最好履行1次remove方法来移除程序。例如移除file以后,封闭窗口之前,提交表单之前,历遍表单元素前等等。既可以节省资源,避免dom的内存泄漏,又能避免表单嵌套时的冲突题目。

【可用性】

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

【编码】

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

【asp版本】

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

使用说明

实例化时,第1个必要参数是file控件对象:new QuickUpload(file);

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

还提供了以下方法:
upload:履行上传操纵;
stop:停止上传操纵;
remove:清除程序。

程序源码

以下为援用的内容:
var QuickUpload = 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(
thisthis._setOptions(options));
};
QuickUpload._counter 
= 1;
QuickUpload.prototype 
= {
  
//设置默许属性
  _setOptions: function(options) {
    
this.options = {//默许值
        action:        "",//设置action
        timeout:    0,//设置超时(秒为单位)
        parameter:    {},//参数对象
        onReady:    function(){},//上传豫备时履行
        onFinish:    function(){},//上传完成时履行
        onStop:        function(){},//上传停止时履行
        onTimeout:    function(){}//上传超时时履行
    };
    
return $$.extend(this.options, options || {});
  },
  
//上传文件
  upload: function() {
    
//停止上1次上传
    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 ) {
        
//创建iframe
        var iframename = "QUICKUPLOAD_" + QuickUpload._counter++,
            iframe 
= document.createElement( $$B.ie ? """ + iframename + "\">" : "iframe");
        iframe.name 
= iframename;
        iframe.style.display 
= "none";
        
//记录完成程序方便移除
        var finish = 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;
        }
        
//插进body
        var body = document.body; body.insertBefore( iframe, body.childNodes[0] );
        
        
this._iframe = iframe;
    }
  },
  
//设置form
  _setForm: function() {
    
if ( !this._form ) {
        
var form = 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"
        });
        
//提交前往掉form
        file.form && $$E.addEvent(file.form, "submit", $$F.bind(this.remove, this));
        
//插进form
        file.parentNode.insertBefore(form, file).appendChild(file);
        
        
this._form = form;
    }
    
//action可能会修改
    this._form.action = this.action;
  },
  
//设置input
  _setInput: function() {
    
var form = this._form, oldInputs = this._inputs, newInputs = {}, name;
    
//设置input
    for ( name in this.parameter ) {
        
var input = form[name];
        
if ( !input ) {
            
//假设没有对应input新建1个
            input = document.createElement("input");
            input.name 
= name; input.type = "hidden";
            form.appendChild(input);
        }
        input.value 
= this.parameter[name];
        
//记录当前input
        newInputs[name] = input;
        
//删除已有记录
        delete oldInputs[name];
    }
    
//移除无用input
    for ( name in oldInputs ) { form.removeChild( oldInputs[name] ); }
    
//保存当前input
    this._inputs = newInputs;
  },
  
//停止上传
  stop: function() {
    
if ( this._sending ) {
        
this._sending = false;
        clearTimeout(
this._timer);
        
//重置iframe
        if ( $$B.opera ) {//opera通过设置src会有题目
            this._removeIframe();
        } 
else {
            
this._iframe.src = "";
        }
        
this.onStop();
    }
  },
  
//清除程序
  remove: function() {
    
this._sending = false;
    clearTimeout(
this._timer);
    
//清除iframe
    if ( $$B.firefox ) {
        setTimeout($$F.bind(
this._removeIframe, this), 0);
    } 
else {
        
this._removeIframe();
    }
    
//清除form
    this._removeForm();
    
//清除dom关联
    this._inputs = this._fFINISH = this.file = null;
  },
  
//清除iframe
  _removeIframe: function() {
    
if ( this._iframe ) {
        
var iframe = 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 ) {
        
var form = 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 = falsethis.stop(); this.onTimeout(); }
  },
  
//完成函数
  _finish: function() {
    
if ( this._sending ) { this._sending = falsethis.onFinish(this._iframe); }
  }
}

完全实例下载

完全实例下载(asp版本)

转载请注明出处:http://www.cnblogs.com/cloudgamer/ 上1页 1 2 3 4 唐山网站建设www.fw8.net
TAG:方法,程序,上传,题目,控件
评论加载中...
内容:
评论者: 验证码: