论坛首页 AJAX版 AJAX

无刷新上传文件以及类Gmail附件添加方式的实现

浏览 4610 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
时间:2007-05-25
好象是第一次在Javaeye发贴,欢迎拍砖.

方案选择:
查阅了一些资料,目前实现实现无刷新上传主要有两种方案,即利用隐藏的iframe来模拟无刷新上传和利用xmlhttp分块上传文件。
这两种方案中,利用隐藏的iframe来模拟无刷新上传用的最为普遍,实现起来也比较容易。而利用xmlhttp分块上传的方式,google
的结果显示用的较少,特别是实用方面,而且代码实现复杂。考虑到要同时实现类似Gmail的附件添加方式,最终选择了利用隐藏的
iframe来模拟无刷新上传的方案。

利用隐藏的iframe来模拟无刷新上传的原理
利用隐藏的iframe来模拟无刷新上传的原理比较简单,在页面中包含一个form和一个iframe,其中ifram设置为不可见,同时将form
的target属性设为iframe的名字,这样当上传的时候,刷新的就是iframe中的页面,而主页面则不会有任何变化。可以在iframe中的
页面中编写上传后客户端要执行的javascript代码,这样可以轻松的实现对主页面的操作。

类Gmail附件添加方式的实现
Gmail的附件添加方式有着比较好的用户体验,原本希望可以通过阅读Gmail的代码来了解Gmail的解决方案,但是发现这个想法有些
不靠谱。所以最终采用自己的方式来解决这个问题并实现了良好的浏览器兼容(IE和Firefox),对于Firefox,是通过利用javascript对DOM的操作,来动
态的创建和删除文件选择框.对于IE,则是结合Javascript,DOM,CSS来实现所要求的效果。在两种浏览器下,均可以进一步扩展,来实现选择后即自动
上传的效果。

DEMO的代码
[code] <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE> New Document </TITLE> <META NAME="Generator" CONTENT="EditPlus"> <META NAME="Author" CONTENT=""> <META NAME="Keywords" CONTENT=""> <META NAME="Description" CONTENT=""> <script type="text/javascript"> /************************ * author lijun * date 2007.5.25 * * 无刷新上传 * IE,Firefox下和Gmail类似的附件添加方式 * 允许进一步扩展,实现附件添加后即自动上传得功能。 * 2007.5.30修正了一个bug. ************************/ /* 检测浏览器类型 */ function isIE() { if(document.attachEvent) { return true; } else { return false; } } /*firefox下的附件添加提示*/ function getFirefoxTip(form) { var str="添加一个附件"; var cssStr="width:100px;font:12px Arial;color:#00f;text-decoration:underline"; var tipDiv=document.createElement("div"); tipDiv.style.cssText=cssStr; tipDiv.innerHTML=str; tipDiv.onclick=function() { var i=form.getAttribute("count")?form.getAttribute("count"):0; createFirefoxInput(form,parseInt(i)+1); }; return tipDiv; } /*删除已经添加的附件项*/ function removeChild(parent,child,tip) { var i=parent.getAttribute("count"); if(isIE()) { var id=parseInt(child.getAttribute("id")); parent.removeChild(child); parent.removeChild(tip); i--; /* var tipAry=new Array(); var inputAry=new Array(); for(j=0;j<parent.childNodes.length;j++) { var node=parent.childNodes[j]; if(node.nodeType==1) { if(node.getAttribute("idi")) { inputAry.push(node); } else if(node.getAttribute("idt")) { tipAry.push(node); } } } for(j=0;j<tipAry.length;j++) { var position=getPosition(tipAry[j]); inputAry[j].style.top=position.top+"px"; inputAry[j].style.left=position.left+"px"; } */ var tipAry=rePlaceInput(parent); if(i==0) { tipAry[i].innerHTML="添加一个附件"; } } else { parent.removeChild(child); i--; if(i==0) { tip.innerHTML="添加一个附件"; } } parent.setAttribute("count",i); } /* 添加移除项*/ function getRemove(form,node,tip) { var text="移除"; var span=document.createElement("span"); span.style.cssText="font:10px Arial;color:#00f;text-decoration:underline;"; span.innerHTML=text; span.onclick=function(){removeChild(form,node,tip);} return span; } /* firefox下的文件选择框*/ function createFirefoxInput(form,inputIndex) { var i=inputIndex?inputIndex:0; var tip=i==0?getFirefoxTip(form):form.lastChild; if(i==0) { form.appendChild(tip); } else { var inputDiv=document.createElement("div"); var input=document.createElement("input"); input.setAttribute("type","file"); input.setAttribute("name","file_"+i); input.onchange=function(){ } inputDiv.appendChild(input); inputDiv.appendChild(getRemove(form,inputDiv,tip)); form.insertBefore(inputDiv,tip); form.setAttribute("count",i); tip.innerHTML="再添加一个附件"; } } /* firefox下的初始化函数*/ function initFirefox() { var form=document.forms['uploadForm']; createFirefoxInput(form); } /* 获取指定元素在页面的位置*/ function getPosition(obj) { var top=0,left=0; while(obj.offsetParent) { top+=obj.offsetTop; left+=obj.offsetLeft; obj=obj.offsetParent; } return {top:top,left:left}; } /*IE下的附件添加提示*/ function getIeTip(form) { var str=parseInt(form.getAttribute("count"))>=0?"再添加一个附件":"添加一个附件"; var cssStr="font:12px Arial;color:#00f;text-decoration:underline"; var tipDiv=document.createElement("div"); tipDiv.style.cssText=cssStr; tipDiv.innerHTML=str; return tipDiv; } /*IE下的文件按选择显示*/ function updateIeInput(input,tip) { var parent=input.parentNode; parent.style.zIndex=-2; tip.style.textDecoration="none"; tip.style.color="#000000"; tip.style.fontWeight="bold"; tip.innerHTML=input.value; tip.appendChild(getRemove(input.form,parent,tip)); } /*创建IE下的文件选择框*/ function createIeInput(form,inputIndex) { var i=inputIndex?inputIndex:0; var tip=getIeTip(form); tip.setAttribute("idt",i) form.appendChild(tip); var inputDiv=document.createElement("div"); var input=document.createElement("input"); input.setAttribute("type","file"); input.setAttribute("name","file_"+i); input.style.cssText="width:0"; input.onchange=function(){ createIeInput(this.form,parseInt(this.form.getAttribute("count"))+1); updateIeInput(this,tip); rePlaceInput(this.form); } inputDiv.appendChild(input); inputDiv.setAttribute("idi",i); var position=getPosition(tip); inputDiv.style.cssText="position:absolute;top:"+position.top+"px;left:"+position.left+"px;filter:alpha(opacity=0);z-index:2"; form.appendChild(inputDiv); form.setAttribute("count",i); } /* 重新置位*/ function rePlaceInput(parent) { var tipAry=new Array(); var inputAry=new Array(); for(j=0;j<parent.childNodes.length;j++) { var node=parent.childNodes[j]; if(node.nodeType==1) { if(node.getAttribute("idi")) { inputAry.push(node); } else if(node.getAttribute("idt")) { tipAry.push(node); } } } for(j=0;j<tipAry.length;j++) { var position=getPosition(tipAry[j]); inputAry[j].style.top=position.top+"px"; inputAry[j].style.left=position.left+"px"; } return tipAry; } /*初始化IE*/ function initIE() { var form=document.forms["uploadForm"]; createIeInput(form); window.onresize=function(){ rePlaceInput(form); } } /*初始化*/ function init() { if(isIE()) { initIE(); } else { initFirefox(); } } </script> </HEAD> <BODY onload="init()"> <form name="uploadForm" action="/upload.do" target="upload" enctype="multipart/form-data" method="post"></form> <iframe name="upload" style="display:none"></iframe> </BODY></HTML> [/code]

总结
目前这个DEMO的客户端效果不错,但是还需要实践的检验。有任何问题欢迎交流。[code][/code][code]
   
时间:2007-05-27
代码不能运行,麻烦测一下先.
   
0 请登录后投票
时间:2007-05-31
lkfnn 写道
代码不能运行,麻烦测一下先.

可否说明你不能运行的具体现象?
   
0 请登录后投票
时间:2007-06-01
这个代码的确不错,不过有两个BUG。
第2行少了个“<”,应该是<html>
第158行,将objobj改为obj。
   
0 请登录后投票
时间:2007-06-01
nbzx 写道
这个代码的确不错,不过有两个BUG。
第2行少了个“<”,应该是<html>
第158行,将objobj改为obj。

这编辑器不好,代码是完整的,但是提交就会有问题。我不知道怎么回事儿。
   
0 请登录后投票
时间:2007-06-04
昨天刚做了个无刷新上传同时无刷新提交表单.
   
0 请登录后投票
时间:2007-10-31
若把上传附件的这个模块放到页面中的某一个具体位置,该怎么操作呢,比如放到一个table里面来,能否把操作的代码发一下。谢谢。
   
0 请登录后投票
时间:2007-10-31
给楼主修改了下,ie隐藏file控件意义不大,使用的是filter在opera有bug,应该使用opacity,firefox也都可以支持的,但是还是不喜欢透明隐藏file控件,给lz修改了下
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">  
<HTML>  
<HEAD>  
 <TITLE> New Document </TITLE>  
 <META NAME="Generator" CONTENT="EditPlus">  
 <META NAME="Author" CONTENT="">  
 <META NAME="Keywords" CONTENT="">  
 <META NAME="Description" CONTENT="">  
 <script type="text/javascript">  
  
 /*附件添加提示*/   
  function getFirefoxTip(form)   
  {   
               var tipDiv=document.createElement("div");   
               tipDiv.style.cssText="width:100px;font:12px Arial;color:#00f;text-decoration:underline";   
               tipDiv.innerHTML="添加一个附件";   
               tipDiv.onclick=function()   
               {   
                       var i=form.getAttribute("count")||0;   
                       createInput(form,parseInt(i)+1);   
               };   
			   form.appendChild(tipDiv);
  }   
  
 /*删除已经添加的附件项*/   
  function removeChild(parent,child)   
  {   
  var i=parent.getAttribute("count");   
  parent.removeChild(child);   
  i--;   
  if(i==0)   
    {   
      parent.lastChild.innerHTML="添加一个附件";   
    }   
  parent.setAttribute("count",i);   
  }   
  
  /* 添加移除项*/   
  function getRemove(form,node)   
  {   
               var span=document.createElement("span");   
               span.style.cssText="font:10px Arial;color:#00f;text-decoration:underline;";   
	           span.innerHTML="移除";   
               span.onclick=function(){removeChild(form,node);}   
               return span;   
  }   
  
  /*文件选择框*/   
  function createInput(form,inputIndex)   
  {   
               var i=inputIndex||0;   
               if(i==0)   
               {   
                       getFirefoxTip(form);;   
               }   
               else   
               {          
                       var inputDiv=document.createElement("div");   
                       var input=document.createElement("input");   
                       input.setAttribute("type","file");   
                       input.setAttribute("name","file_"+i);   
                       inputDiv.appendChild(input);   
                       inputDiv.appendChild(getRemove(form,inputDiv));   
                       form.insertBefore(inputDiv,form.lastChild);   
                       form.setAttribute("count",i);   
                       form.lastChild.innerHTML="再添加一个附件";   
               }   
                  
       }   
  /*初始化*/   
  function init()   
  {   
       createInput(document.forms['uploadForm']);   
  }   
 </script>  
</HEAD>  
  
<BODY onload="init()">  
<form name="uploadForm" action="/upload.do" target="upload" enctype="multipart/form-data" method="post"></form>  
<iframe name="upload" style="display:none"></iframe>  
</BODY></HTML>  

可能更加清晰点,没有什么授权问题吧
   
0 请登录后投票
时间:2007-10-31
想请教一下怎样用xmlhttp实现文件分块上传。javascript没有读文件的能力,如何去分块读一个文件,再传给服务器啊。
超出偶的理解范围了。。。之前一直相信iframe是唯一的方法
   
0 请登录后投票
论坛首页 AJAX版 AJAX

跳转论坛:
JavaEye推荐