[原创 flex] flex制作的多文件上传组件

效果演示地址:http://www.adanghome.com/upload/  

源文件下载地址:http://www.adanghome.com/flash_upload.rar

下载后导入flexbuilder,即可按需修改。

最近在研究flex,这方面资料很少。从《程序员》杂志上看到《flex第一步》出版的时候,超高兴,周末就跑去买了。只是因为工作太忙,一直没机会学。现在项目已经基本完成了,我总算有时间来研究它了。

    项目中需要增加一个flash上传的组件,实现多文件上传,同时显示上传进度。样式要像flickr或者海内一样。首先,我想到了去网上down一个。结果找来找去也没找到好用的,而且就算找到了,改变样式等等又会是麻烦的事情。算了,还是自己做一个吧。因为是第一次使用flex,也是第一次使用as3,所以这个组件用了一周的时间才完成。

    先放上代码吧。

这是主mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="495" height="380" fontSize="12" backgroundAlpha="0" applicationComplete="initApp()">
<mx:Script>
    <![CDATA[
     import mx.events.IndexChangedEvent;
     import mx.events.CloseEvent;
     import flash.net.FileReference;
     import flash.net.FileReferenceList;
     import flash.net.FileFilter;
     import flash.events.Event;
     import flash.events.MouseEvent;
     import flash.net.URLRequest;
     import mx.controls.Alert;
     import mx.events.CloseEvent;
     import flash.external.ExternalInterface;   
     public var fileRef:FileReferenceList = new FileReferenceList();      
     public var uploadImg_num:int = 0;
     public var upload_size:Number = 0;
     public var upload_size_total:Number = 0;        
     private var info:Array = new Array();
   
     //===================
     // 功能设置
     //===================
      
     /* 允许一次性上传的最大图片数 ********************/
     private var upload_maxCount:int = 50;
     /* 上传提交的服务器端文件路径 ********************/
     private var severURL:String = "photos.php?op=add_photo&tag=abc&album=0&gid=0&party_id=0&attr=0";
     /* 服务器端接收用的名称 ********************/
     private var fileName:String = "file1";
   
   
     public var urlRequest:URLRequest = new URLRequest(severURL);
     //===================
     // 浏览本地文件
     //===================
     internal function browseHandler(e:Event):void{
      var imageTypes:FileFilter = new FileFilter("Images(*.jpg,*.jpeg,*.gif,*.png)","*.jpg;*.jpeg;*.gif;*.png");
      var allTypes:Array = new Array(imageTypes);    
      fileRef.browse(allTypes);    
     }
   
     //===================
     // 选中文件,关闭对话框
     //===================
     internal function selectHandler(e:Event):void{       
      var imageNum:int = fileRef.fileList.length;
      if(imageNum>upload_maxCount){
       Alert.show("一次最多只能上传"+upload_maxCount+"张图片","提示信息");
       return;
      }
      info = new Array(); 
      var sizeMum:Number = 0;
      delete_btn.enabled = true;
      add_btn.enabled = true;
      for(var i:Number=0;i<imageNum;i++){
       info.push({label:fileRef.fileList[i].name,data:fileRef.fileList[i].size/1000+"KB",width:0});
       sizeMum+=fileRef.fileList[i].size;     
      }   
      process_list.dataProvider = info;
      tip_txt.text="共"+imageNum+"张图片,"+(sizeMum/(1000*1000)).toString().slice(0,-4)+"MB";
     }
   
     //===================
     // 单个文件上传结束
     //===================
     internal function completeHandler():void{
      fileRef.fileList[uploadImg_num].removeEventListener(ProgressEvent.PROGRESS,onProcessHandler);
      fileRef.fileList[uploadImg_num].removeEventListener(Event.COMPLETE,onComplete); 
      upload_size+=fileRef.fileList[uploadImg_num].size;
      processBar_Total.width=(upload_size/upload_size_total)*488;
      tip_txt.text="已上传" + int(upload_size*100/upload_size_total) + "% (" + (uploadImg_num+1) +"/" + fileRef.fileList.length + ")";
      uploadImg_num++;
      process_list.scrollToIndex(uploadImg_num);
      if(uploadImg_num>=fileRef.fileList.length){
      // ExternalInterface.call("uploadCompelete");
       return;
      } 
      upLoadImg(uploadImg_num);
     }
     internal function onComplete(e:Event):void{
      completeHandler();
     // Alert.show(e.toString());
     }
   
     //===================
     // 监听上传进度
     //===================
     internal function onProcessHandler(e:ProgressEvent):void{     
      var proc:uint = e.bytesLoaded/e.bytesTotal*100;
      var num:Number = uploadImg_num;    
      if(process_list.indexToItemRenderer(num)!=null)
      {
       process_list.indexToItemRenderer(num).document.processBar.width = proc*0.01*245;
       info[num].width = proc*0.01*245;          
      }   
     }
   
     //===================
     // 执行上传操作
     //===================
     internal function upLoadImg(oNum:int):void{
      try
      { 
       fileRef.fileList[oNum].upload(urlRequest,fileName);
       fileRef.fileList[oNum].addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,onComplete);      
       fileRef.fileList[oNum].addEventListener(ProgressEvent.PROGRESS,onProcessHandler); 
      }
      catch(e:Event){
       Alert.show("上传出错","提示信息");
       completeHandler();
      }    
     }
   
     //===================
     // 点击上传按钮
     //===================
     internal function uploadHandler():void{
      if(uploadImg_num!=0) return;
      if(process_list.dataProvider==null || info.length<=0){
       Alert.show("您还未选择图片!","提示信息");
       return;
      }  
      for(var i:Number=0;i<fileRef.fileList.length;i++){    
       upload_size_total+=fileRef.fileList[i].size;          
      }         
      upLoadImg(uploadImg_num);
      delete_btn.enabled = false;
      add_btn.enabled = false;
      browse_btn.enabled = false;   
     }
   
     //===================
     // 执行删除操作
     //===================
     internal function deleteHandler(e:Event):void{
      if(process_list.selectedIndices.length<=0){
       Alert.show("您还没有选择图片","提示信息");
       return;
      }
      Alert.show("您确定要删除选定图片?","提示信息",Alert.YES|Alert.NO,process_list,alertHandler,null,Alert.NO);
      function alertHandler(evt:CloseEvent):void{
       if(evt.detail==Alert.YES){
        deleteItem();
       } else {
            
       }
      }
      function deleteItem():void{
       var selectItems:Array = process_list.selectedItems;
       var selectIndex:Array = process_list.selectedIndices;
       var iCount:int = selectItems.length;
       var sizeMum:Number = 0;
       for(var i:int=0;i<iCount;i++){
        info.splice(selectIndex[i],1);
        fileRef.fileList.splice(selectIndex[i],1);
       }
       for(var j:Number=0;j<fileRef.fileList.length;j++){      
        sizeMum+=fileRef.fileList[j].size;     
       }   
       process_list.dataProvider = info;
       tip_txt.text="共"+fileRef.fileList.length+"张图片,"+(sizeMum/(1000*1000)).toString().slice(0,-4)+"MB";
          
       if(info.length<=0){
        delete_btn.enabled = false;
       }     
      }
     } 
   
     //===================
     // 执行添加操作
     //===================
     internal function addHandler(e:Event):void{
      var imageTypes:FileFilter = new FileFilter("Images(*.jpg,*.jpeg,*.gif,*.png)","*.jpg;*.jpeg;*.gif;*.png");
      var allTypes:Array = new Array(imageTypes);
      var addFileRef:FileReferenceList = new FileReferenceList();
      var sameImgIndex:Array = new Array();
      var beforeAddLength:Number = fileRef.fileList.length;    
      addFileRef.browse(allTypes);
      addFileRef.addEventListener(Event.SELECT,addSelectHandler);
    
      function addSelectHandler():void{          
       if(addFileRef.fileList.length+fileRef.fileList.length>upload_maxCount){
        Alert.show("一次最多只能上传"+upload_maxCount+"张图片","提示信息");
        return;
       }     
       for(var i:int=0;i<addFileRef.fileList.length;i++){
        for(var j:int=0;j<fileRef.fileList.length;j++){
         if(fileRef.fileList[j].name==addFileRef.fileList[i].name){
          sameImgIndex.push(i);        
         }
        }
       }    
       for(var k:Number=0;k<addFileRef.fileList.length;k++){ 
        fileRef.fileList.push(addFileRef.fileList[k]);
        info.push({label:addFileRef.fileList[k].name,data:addFileRef.fileList[k].size/1000+"KB",width:0});
       }
       for(var m:Number=0;m<sameImgIndex.length;m++){
        fileRef.fileList.splice(beforeAddLength+sameImgIndex[m]-m,1);
        info.splice(beforeAddLength+sameImgIndex[m]-m,1);
       }     
       var imageNum:int = fileRef.fileList.length;
       var sizeMum:Number = 0;
       for(var l:Number=0;l<imageNum;l++){      
        sizeMum+=fileRef.fileList[l].size;     
       }   
       process_list.dataProvider = info;
       tip_txt.text="共"+imageNum+"张图片,"+(sizeMum/(1000*1000)).toString().slice(0,-4)+"MB";
       delete_btn.enabled = true; 
      }    
     }
   
     //===================
     // 初始化函数
     //===================  
     internal function initApp():void{
      browse_btn.addEventListener(MouseEvent.CLICK,browseHandler);
      fileRef.addEventListener(Event.SELECT,selectHandler);
      delete_btn.addEventListener(MouseEvent.CLICK,deleteHandler);
      add_btn.addEventListener(MouseEvent.CLICK,addHandler); 
      /* 注册供js调用的函数 ********************/
      ExternalInterface.addCallback("uploadImg",uploadHandler);  
     }
    ]]>
</mx:Script>
<mx:Canvas>
    <mx:Button label="浏览文件" id="browse_btn"/>
    <mx:Label text="按住 Ctrl 或 Shift 键可以多选" x="85" y="3"/>
    <mx:Button label="添加文件" x="330" id="add_btn" enabled="false"/>
    <mx:Button label="删除文件" x="410" id="delete_btn" enabled="false"/>
    <mx:Canvas width="490" height="25" backgroundColor="0Xf1f1f1" x="0" y="43" borderStyle="solid" borderColor="0Xbbbbbb">
     <mx:Label text="文件名" x="5" y="3"/>
     <mx:Label text="文件大小" x="215" y="3"/>
     <mx:Label text="上传进度" x="320" y="3"/>
    </mx:Canvas> 
    <mx:List x="0" y="67" width="490" rowCount="10" allowMultipleSelection="true" borderStyle="solid" borderColor="0Xbbbbbb" rowHeight="25" itemRenderer="ImageItem" id="process_list"/>
    <mx:Canvas width="490" height="25" backgroundColor="0Xf1f1f1" x="0" y="318" borderStyle="solid" borderColor="0Xbbbbbb">
     <mx:Label text="" fontWeight="bold" id="tip_txt" x="5" y="3"/>   
    </mx:Canvas> 
    <mx:Canvas borderStyle="solid" x="0" width="490" y="350" height="25" borderColor="0X124fc0" backgroundColor="0xffffff">
     <mx:Canvas backgroundColor="0X124fc0" backgroundAlpha="0.5" id="processBar_Total" width="0" height="23"/>
    </mx:Canvas>   
</mx:Canvas> 
</mx:Application>

这个是ImageItem.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" height="25" borderSides="bottom" borderStyle="solid"> 
<mx:Label width="195" text="{data.label}" x="5"/>
<mx:Label width="100" text="{data.data}" x="210"/>
<mx:Canvas width="145" borderStyle="solid" borderColor="0X124fc0" height="7" x="320" y="5">
    <mx:Canvas width="{data.width}" id="processBar" height="7" backgroundColor="0X124fc0" backgroundAlpha="0.5"/>
</mx:Canvas>
</mx:Canvas>

然后是html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
*{margin:0px;padding:0px;}
#upload_flash{width:495px;height:380px;}
</style>
<script type="text/javascript">

//上传完成后的回调函数
function uploadCompelete(){
/location.href=http://www.baidu.com;
}


function submitForm(){
    thisMovie("upload").uploadImg();
}
function thisMovie(movieName) {
    if (navigator.appName.indexOf("Microsoft") != -1) {
     return window[movieName];
    } else {
     return document[movieName];
    }
}

</script>
</head>

<body>
<div id="upload_flash">
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
     id="upload" width="100%" height="100%"
     codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
     <param name="wmode" value="transparent">
     <param name="movie" value="upload.swf" />
     <param name="quality" value="high" />
     <param name="bgcolor" value="#869ca7" />
     <param name="allowScriptAccess" value="sameDomain" />
     <embed src="upload.swf" quality="high" bgcolor="#869ca7" wmode="transparent"
      width="100%" height="100%" name="upload" align="middle"
      play="true"
      loop="false"
      quality="high"
      allowScriptAccess="sameDomain"
      type="application/x-shockwave-flash"
      pluginspage="http://www.adobe.com/go/getflashplayer">
     </embed>
</object>
</div>
<p><input type="button" value="提交" onclick="submitForm()" /></p>
</body>
</html>

最后是服务器端,服务器端很简单拉。我就放个php的吧:

<?php
//create the directory if doesn't exists (should have write permissons)
if(!is_dir("./files")) mkdir("./files", 0755); 
//move the uploaded file
move_uploaded_file($_FILES['Filedata']['tmp_name'], "./files/".$_FILES['Filedata']['name']);
echo "aaa";
?>

===========================================================================

哈哈,效果还是蛮不错的。这里要说几个细节问题。很重要的心得:

1) as3的传值问题。 as3对事件监听只能使用函数名,不能传值,这是个很郁闷的地方。在网上搜过解决方案。大概有两种思路。一种是自定义一个类,扩展event,然后使用自己定义的类来监听事件。这个方法呢,我找了半天也没找到个具体的代码,很不好用,烦。另一种呢,是对触发事件的对象添加一个自己定义的属性,捆在对象上。然后呢,event.target来取得这个对象,再访问这个对象的这个属性。这种方法在写js的时候,我经常用,是很好用的方法。对象就当做是个容器,容器绑定我需要的变量,哈哈,超好用。只是,as3好像对对象扩展属性有限制,不能像js那样随心所欲地添加,需要先扩展一下类,然后才能对相应的对象添加属性。可是,超超超奇怪的是,这样一来,所有对象的这个属性居然指向同一地址!!!天哪,难道实例化对象之后,不是每个对象的属性都应该分配一个单纯的地址吗??太匪夷所思了。最后,这种方法也没给我帮上忙。只能利用全局的变量来实现参数传递了。可是,这哪里算传参呢?好在我这个功能不算复杂,如果复杂一点的功能,恐怕就惨了。

2)list组件的itemRenderer的访问。 在flash里,我们如果要访问父级元素的子级元素很简单,只需要parent_mc.child_mc.child_mc就可以访问到任意层级的子元素。可是flex里的机制不一样。比如说这个itemRenderer应该如何访问。我首先想到,list组件会不会用一个item数组来表示它里面的元素呢?比如,如果访问第2个元素里的adang_mc,我就用list.item[1].adang_mc来访问它。结果flexbuilder里压根就没有相应的属性提示。后来在一英文网站看到有人提了相同的问题,才知道原来是利用.indexToItemRenderer(num).document来访问子元素的。

3)list组件itemRenderer的渲染问题。 本以为只要用循环去一个一个访问就可以访问到所有itemRenderer里面的元素了。结果发现根本不是这么回事,只有可视范围内的itemRenderer才会被渲染,不对,更确切地说,只有可视范围内的itemRenderer才能被访问!!! 也就是说,如果你的list有二十个元素,而可视范围只有十个,当你访问第十一个的时候,它返回值为空!当前把滚动条拉下来时,那些本不可见的元素在可见范围的话,就可以访问了,而且本可见,现在被滚进去的元素现在又不能访问了。my god!!!这样编程多麻烦啊!我猜可能当初flex在设计list组件的时候,想尽量减少内存的开销吧,可是,这也太麻烦了吧??有没有解决办法呢?有。我想到的是,自动进行滚动,在flex中有相应的代码,那就是.scrollToIndex(index)。只是这个方法并会在index出现在可视范围外的时候才会调用,这个也是没想到的,不过并不影响我们的功能。

posted on 2008-04-18 11:14  真阿当  阅读(123)  评论(0编辑  收藏  举报