如何实现FLEX+Webservice 大附件上传、断点续传知多少:


实现思路
大附件上传,如何流畅不占用内存,还要支持断点续传,当第一次看到这些需求的时候还是有所顾虑,传统ASP.NET中利用fileupload可以实现上传,但是webconfig中文件大小受限制,即使设置大小了也将面临超时的问题。对于上述情况,WINFORM应该能够很好的解决断点续传大文件,当应用到WEB应用中的时候就很难如此轻松了,因此富客户端思想是很好的选择,决定采用FLEX实现客户端,Webservice实现客户端。
由于ASP.NET默认支持4M大小文件上传,一次需要将需要上传的文件进行分割,客户端分块上传,服务端分块追加。
具体实现
1)、客户端实现

  客户端界面设计

 

<s:Group  width="100%" height="100%">
        
<s:Button id="btnBrower" x="390" y="25" label="浏览..." width="60" click="btnBrower_clickHandler(event)"></s:Button>
        
<s:TextInput id="edFile" x="21" y="24" width="361" enabled="false"/>
        
<s:Button id="btnUpload" x="458" y="25" label="开始上传" width="71" click="btnUpload_clickHandler(event)"/>
        
<mx:Canvas width="508" height="25" backgroundColor="0Xf1f1f1" x="21" y="53" borderStyle="solid" borderColor="0Xbbbbbb">
            
<mx:Label text="" fontWeight="bold" id="tip_txt" x="5" y="4"/>            
        
</mx:Canvas>
        
<mx:Canvas id="totalProcess" borderStyle="solid" x="22" width="507" y="82" height="13" borderColor="0X124fc0" backgroundColor="0xffffff">
            
<mx:Canvas backgroundColor="0X124fc0" backgroundAlpha="0.5" id="processBar_Total" width="0" height="23"/>
        
</mx:Canvas>
        
    
</s:Group>


  定义Webservice方法,对应服务端,各自有自己的返回事件

 

<s:WebService id="service" wsdl="../Service.asmx?wsdl" useProxy="false">
            
<s:operation name="WriteFile" result="onResult(event)" fault="onFault(event)">                
            
</s:operation>
            
<s:operation name="CheckFile" result="onCheckResult(event)" fault="onCheckFault(event)">                
            
</s:operation>            
            
<s:operation name="CopyFile" result="onCopyResult(event)" fault="onCopyFault(event)">                
            
</s:operation>
        
</s:WebService>

  浏览需要上传的文件

 

      //页面加载完毕,进行相关事件注册
            protected function application1_creationCompleteHandler(event:FlexEvent):void
            {
                
// 注册调用js函数
                ExternalInterface.addCallback("getFileInfo",GetFileInfo);
                
                
//初始化文件浏览事件
                file.addEventListener(Event.SELECT,onSelect);//选择文件事件
                file.addEventListener(Event.COMPLETE,onComplete);//文件加载完毕
                file.addEventListener(Event.OPEN,onOpen);
                btnUpload.enabled
=false;
            }
            
            //浏览文件
            private function onSelect(evt:Event):void
            {
                
this.tip_txt.text="";    
                edFile.text
=file.name;
                
//浏览完成,开始加载
                file.load();
            }
            
//加载文件完毕
            private function onComplete(evt:Event):void
            {    
                
this.tip_txt.text="加载完毕";            
                btnUpload.enabled
=true;
            }
            
            private 
function onOpen(evt:Event):void
            {
                
this.tip_txt.text="正在加载...";
            }


  校验服务端文件是否存在,不存在则创建,存在则判断是否需要断点续传

 

       //校验文件函数
            private function CheckFile():void
            {
                
//调用webservice方法,传递文件名进行校验
                service.CheckFile.send(file.name);
            }
       
       //webervice,校验成功
            private function onCheckResult(event:ResultEvent):void
            {
                
//获取返回值
                var retArray:Array=new Array();
                retArray
=event.result.toString().split(",");
                
var retMsg:String=retArray[0].toString();//返回文件存在与否消息
                var retNum:int=int(retArray[1].toString());//返回文件大小                
                tip_txt.text=retMsg;
                
                
//判断是否存在文件
                if(retNum != 0)
                {
                    
//若文件已经存在,判断文件是否已经上传完毕
                    if(retNum == file.data.length)
                        tip_txt.text
="文件已上传完毕";
                    
else
                    {
                        tip_txt.text
="准备断点续传";
                        
                        
//断点续传需要重新计算块数,剩余大小
                        var Leave:int= file.data.length-retNum;
                        
                        
//判断剩余情况
                        if(Leave>blocksize)
                        {
                            
//剩余部分分块    
                            var BlockNum2:Number=(Leave / blocksize);
                            BlockNum
=int(BlockNum2);
                            BlockNumles
=int(BlockNum2);
                            reBlock
=Leave % blocksize;                            
                            tip_txt.text
="正在处理...";
                            
//调用上传函数
                            uploadFile(retNum,blocksize);                            
                        }
                        
else
                        {
                            
//直接从返回值大小开始,传递剩余部分
                            BlockNumles=1;
                            uploadFile(retNum,reBlock);
                        }                                    
                    }
                }
                
else
                {
                    
//若文件不存在,则创建,从0开始
                    var BlockNum1:Number=(file.data.length / blocksize);
                    BlockNum
=int(BlockNum1);
                    BlockNumles
=int(BlockNum1);
                    reBlock
=file.data.length % blocksize;
                    
                    tip_txt.text
="正在处理...";    
                    uploadFile(retNum,blocksize);
                }                
            }
            
            private 
function onCheckFault(event:FaultEvent):void
            {
                Alert.show(event.toString());
            }
            
           


  开始上传

 

        //上传附件函数
            private function uploadFile(begin:int,end:int):void
            {                
                
//判断文件大小
                if(file.data.length>blocksize)
                {
                    
//读取部分文件,分块上传
                    fileUpload.writeBytes(file.data,begin,end);
                    service.WriteFile.send(file.name,fileUpload);
                }
                
else
                {                    
                    
//直接上传
                    service.WriteFile.send(file.name,fileUpload);
                }                                                
            }

      //webservice相关事件函数,上传成功
            private function onResult(event:ResultEvent):void
            {
                
//每次上传成功返回值,作为下次传递的开始位置
                var begin:int=int(event.result.toString());
                BlockNumles
-=1;//递减
                BlockNumadd+=1;//递增
                
//清空历史数据
                fileUpload.clear();
                
                
//进度条
                onProgress(begin,file.data.length);
                
//判断剩余块多少,进行不同情况的上传
                if(BlockNumles>0)
                {
                    uploadFile(begin,blocksize);
                }
                
                
if(BlockNumles==0)
                {
                    uploadFile(begin,file.data.length
-begin);
                    tip_txt.text
="上传完毕!";
                    tip_txt.text
="开始扫描文件...";
                    service.CopyFile.send(file.name);
                }                
            }
            
//上传失败
            private function onFault(event:FaultEvent):void
            {
                Alert.show(
event.toString());
            }

进度监视

 

//上传进度条
            private function onProgress(Loaded:int,Total:int):void
            {
                processBar_Total.width
=(Loaded/Total)*506;                
                tip_txt.text
="已上传:" + Loaded+'/'+Total;
                
if(Loaded==Total)
                    tip_txt.text
="已上传完毕";
            }    


  上传完毕处理文件,完毕之后需要对文件进行类似处理,在这里是对文件进行重命名。具体在客户端可以体现出来

 

//复制文件
            private function onCopyResult(event:ResultEvent):void
            {
                tip_txt.text
="扫描完成";
                
//文件上传结束,调用js函数
                var f:String = "showButton";
                var m:String 
= ExternalInterface.call(f);
                trace(m); 
            }
            
            
private function onCopyFault(event:FaultEvent):void
            {
                Alert.show(
event.toString());
            }

 

2)、服务端实现
  对应客户端三个方法实现,分别是校验、上传、上传完毕
  校验文件,并返回值

 

    #region 校验文件是否存在
    [WebMethod]
    
public string CheckFile(string FileName)
    {
        
string FileSavePath = Server.MapPath("File/"+ FileName;
        
if (!IsExistFile(FileSavePath))
            
return "文件不存在,0";
        
else
        {
            
string FileSize = GetFileSize(FileSavePath).ToString();
            
return "文件已存在," + FileSize;
        }
    }
    
#endregion


  开始写文件,没有则创建,有则追加

 

    #region 写文件
    [WebMethod]
    
public string WriteFile(string FileName, byte[] filestrem)
    {
        
string FileSavePath = Server.MapPath("File/"+ FileName + ".temp";
        
if (!IsExistFile(FileSavePath))
        {
            FileStream fs 
= new FileStream(FileSavePath, FileMode.Create);
            
//获得字节数组
            byte[] data = filestrem;
            
//开始写入
            fs.Write(data, 0, data.Length);
            
//清空缓冲区、关闭流
            fs.Flush();
            fs.Close();
        }
        
else
        {
            
//追加文件
            using (System.IO.FileStream f = new System.IO.FileStream(FileSavePath, System.IO.FileMode.Append, FileAccess.Write))
            {
                
byte[] b = filestrem;
                f.Write(b, 
0, b.Length);
            }
        }

        
return GetFileSize(FileSavePath).ToString();
    }
    
#endregion


  上传完毕

 

#region
    [WebMethod]
    
public string CopyFile(string FileName)
    {
        
string FileSavePath = Server.MapPath("File/"+ FileName+".temp";
        
string FileConvertPath = Server.MapPath("ConvertFile/"+ FileName;

        
//如果目标中存在同名文件,则删除   
        if (IsExistFile(FileConvertPath))
        {
            DeleteFile(FileConvertPath);
        }
        
//将文件复制到指定目录   
        File.Copy(FileSavePath, FileConvertPath);

        
//删除原始临时文件
        DeleteFile(FileSavePath);

        
string Path = FileConvertPath;
        
return Path;
    }
    
#endregion


存在问题
客户端要把文件读取完毕之后,才开始分段上传,如果文件过大,内存玩儿不转那么浏览器将会死掉。需要继续改进,请大家拍砖!!源代码全部奉上,了解flex的可以看看flex部分源码,不了解的可以直接在项目中使用,已经在.NET项目中配置完毕,可直接运行看到效果。本例子仅为beta1.0版本,还在继续修改当中。

效果图:

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"