文件传送,如此简单--ESFramework 4.0 快速上手(13)

简介: 在所有的通信系统中,文件传送是最常见也是最重要的功能之一,ESFramework对文件传送的强大支持也是其亮点之一,使用ESFramework可以非常轻松地实现与文件传送相关的所有需求。ESPlus.Application.FileTransfering命名空间完整地解决了通信中与文件收发相关的问题,可以支持客户端与客户端之间的文件对传、上传文件到服务器以及从服务器下载文件,并且可以监控每个文件传送的实时状态、且内置了文件续传等功能。

      在所有的通信系统中,文件传送是最常见也是最重要的功能之一,ESFramework对文件传送的强大支持也是其亮点之一,使用ESFramework可以非常轻松地实现与文件传送相关的所有需求。ESPlus.Application.FileTransfering命名空间完整地解决了通信中与文件收发相关的问题,可以支持客户端与客户端之间的文件对传、上传文件到服务器以及从服务器下载文件,并且可以监控每个文件传送的实时状态、且内置了文件续传等功能。

 

一.ESPlus的文件传送流程

  ESPlus定义了文件传送的标准流程,可以用下图表示:

   

(1)由发送方发起传送文件的请求。

(2)接收方回复同意或者拒绝接收文件。如果拒收,则流程结束;否则进入下一步。

(3)发送方发送文件数据,接收方接收文件数据。

(4)如果文件传送过程中,接收方或发送方掉线或者取消文件传送,则文件传送被中断,流程结束。如果文件传送过程一直正常,则到最后完成文件的传送。 

      有几点需要说明一下:

(1)发送方可以是客户端,也可以是服务器;接收方也是如此。但无论发送方和接收方的类别如何,它们都遵守这一文件传送流程;就像ESFramework所有的通信引擎都公用同一套消息处理骨架流程一样。

(2)当接收方同意接收后,框架会自动搜索是否存在匹配的续传项目,若存在,则会启动断点续传。当然,我们可以通过文件接收管理器的属性来控制断点续传功能是否开启。关于断点续传的更多内容,可以参考ESFramework 4.0 文件断点续传原理与实现

(3)进行文件传送的线程是由框架自动控制的,只要发送方收到了接收方同意接收的回复,框架就会自动在后台线程中发送文件数据包;同样,此时接收方也会自动处理接收到的文件数据包。

(4)发送方或接收方都可随时取消正在传送的文件。

(5)当文件传送被中断或完成时,发送方和接收方都会有相应的事件通知。

 

二.ESPlus用于支持文件传送的基础设施

1.TransmittingFileInfo

  无论是发送方还是接收方,针对每个文件传送任务,都需要有个对象来表示它,TransmittingFileInfo便是一个文件传送项目的封装,里面包含了类似发送者ID、接收者ID、文件名称等相关信息。

     

   TransmittingFileInfo的大部分属性对于发送方和接收方都是有效的,而有几个属性只对发送方有效(比如SendingFileParas),有几个属性只对接收方有效(如LocalSaveFilePath),这些在帮助文档中都有详细的说明。而且,有些属性(如OriginFileLastUpdateTime)的存在是用于支持断点续传功能的。

 

2.FileTransDisrupttedType

    ESPlus使用FileTransDisrupttedType枚举定义了所有可能导致文件传送中断的原因:

     public   enum   FileTransDisrupttedType
    {
        
///   <summary>
        
///  自己主动取消
        
///   </summary>
        ActiveCancel,
        
///   <summary>
        
///  对方取消
        
///   </summary>
        DestCancel,
        
///   <summary>
        
///  对方掉线
        
///   </summary>
        DestOffline,
        
///   <summary>
        
///  网络中断、自己掉线
        
///   </summary>
        SelfOffline,
        
///   <summary>
        
///  对方拒绝接收文件
        
///   </summary>
        DestReject,
        
///   <summary>
        
///  其它原因,如文件读取失败等
        
///   </summary>
        OtherCause
    }

        

3.IFileTransferingEvents 接口

       ESPlus定义了IFileTransferingEvents接口,用于暴露所有与文件传送相关的状态和事件:

    public   interface   IFileTransferingEvents
    {
        
///   <summary>
        
///  当某个文件开始传送时,触发该事件。
        
///   </summary>
         event   CbGeneric < TransmittingFileInfo >  FileTransStarted;

        
///   <summary>
        
///  当某个文件续传开始时,触发该事件。(将不再触发FileTransStarted事件)
        
///   </summary>
         event   CbGeneric < TransmittingFileInfo >  FileResumedTransStarted;

        
///   <summary>
        
///  文件传送的进度。参数为fileID(文件编号) ,total(文件大小) ,transfered(已传送字节数)
        
///   </summary>
         event   CbFileSendedProgress  FileTransProgress; 
        
        
///   <summary>
        
///  文件传送中断时,触发该事件。
        
///   </summary>
         event   CbGeneric < TransmittingFileInfo FileTransDisrupttedType >  FileTransDisruptted;

        
///   <summary>
        
///  文件传送完成时,触发该事件。
        
///   </summary>
         event   CbGeneric < TransmittingFileInfo >  FileTransCompleted; 
    }

      通过预定这些事件,我们可以知道每个传送的文件什么时候开始(或断点续传)、什么时候完成、传递的实时进度、传送中断的原因等等。要注意的是,这些事件都是在后台线程中触发的,如果在事件处理函数中需要更新UI,则需要将调用转发到UI线程。

 

4.SendingFileParas

      该对象仅仅包含两个属性:SendingSpanInMSecs和FilePackageSize。发送方可以通过SendingFileParas对象来指定发送文件数据包时的频率与每个数据包的大小。一般来说,为了达到最快的传送速度,SendingSpanInMSecs可以设为0。而FilePackageSize的大小则要根据发送方与接收方的网络环境的好坏进行决定,在Internet上,一般可以设为2048或4096左右;而在局网内,可以设为204800甚至更大(在局网的传送速度可以达到30M/s以上)。

 

5.IFileController

      通过ESPlus.Application.FileTransfering.IFileController接口,我们可以提交发送文件的请求,并且可以主动取消正在接收或发送的文件。IFileController即可用于客户端也可用户服务端。

    public   interface   IFileController
    {
        
///   <summary>
        
///  该事件接口暴露了所有正在发送文件的实时状态。
        
///   </summary>
         IFileTransferingEvents  FileSendingEvents {  get ; }

        
///   <summary>
        
///  该事件接口暴露了所有正在接收的文件的实时状态。
        
///   </summary>
         IFileTransferingEvents  FileReceivingEvents {  get ; }

        
///   <summary>
        
///  准备发送文件。如果对方同意接收,则后台会自动发送文件;如果对方拒绝接收,则会取消发送。
         ///  如果要发送的文件不存在或被占用,则将抛出对应的异常。
        
///   </summary>       
        
///   <param name="accepterID"> 接收文件的用户ID </param>
        
///   <param name="filePath"> 被发送文件的路径 </param>
        /
//   <param name="comment"> 其它附加备注。如果是在类似FTP的服务中,该参数可以是保存文件的路径 </param>        
        
///   <param name="fileID"> 返回即将发送文件的唯一编号 </param>
         void  BeginSendFile( string  accepterID,  string  filePath,  string  comment,  out   string  fileID);

        
///   <summary>
        
///  准备发送文件。如果对方同意接收,则后台会自动发送文件;如果对方拒绝接收,则会取消发送。
         ///  如果要发送的文件不存在或被占用,则将抛出对应的异常。
        
///   </summary>
        
///   <param name="accepterID"> 接收文件的用户ID </param>
        
///   <param name="filePath"> 被发送文件的路径 </param>
        
///   <param name="comment"> 其它附加备注。如果是在类似FTP的服务中,该参数可以是保存文件的路径 </param>
        
///   <param name="paras"> 发送参数设定。传入null,表示采用IFileSenderManager的默认设置。 </param>
        
///   <param name="fileID"> 返回即将发送文件的唯一编号 </param>
         void  BeginSendFile( string  accepterID,  string  filePath,  string  comment, SendingFileParas  paras,  out   string  fileID);
       
        
///   <summary>
        
///  主动取消正在发送或接收的文件,并通知对方。
         ///   </summary>         
         void  CancelFileTransfering( string  fileID);    

        
///   <summary>
        
///  取消与某个用户相关的正在传送项目。
         ///   </summary>        
        /// <param name="destUserID">目标用户ID。</param>
         void  CancelFileTransferingAbout( string  destUserID);    

        
///   <summary>
        
///  获取正在发送或接收中的文件的相关信息。
        
///   </summary>        
         TransmittingFileInfo  GetFileInfo( string  fileID);
    
}

(1)BeginSendFile用于向接收方提交发送文件的请求,如果对方同意,则后台会自动开始传递文件。该方法有个out参数fileID,用于传出标记该文件传送项目的唯一编号,比如,你打算将同一个文件发送给两个好友,将会调用两次BeginSendFile方法,而两次得到的fileID是不一样的。也就是说,fileID是用于标记文件传送项目的,而不是标记文件的。

      该方法有两个重载,区别在于第二个BeginSendFile方法多了一个SendingFileParas参数,用于主动控制文件数据包的大小和发送频率。     

      在客户端使用时,BeginSendFile方法不仅可以向其他在线用户提交发送文件的请求,也可以直接向服务器提交发送文件的请求 -- 即此时文件的接收者为服务端。我们只需要将accepterID参数传入NetServer.SystemUserID,以指明由服务端而不是其他用户来接收即将发送的文件。

(2)GetFileInfo方法可以获取任何一个正在发送或正在接收的项目信息。

(3)CancelFileTransfering方法用于取消正在发送或接收的某个文件传送项目,调用该方法时,框架会自动通知文件传送的另一端用户,并触发FileReceivingEvents或FileSendingEvents中的FileTransDisruptted事件,而另一端也会自动触发FileTransDisruptted事件。

(4)CancelFileTransferingAbout方法用于取消与某个指定用户相关的正在传送项目。比如,我们正在与aa01用户聊天,并且与aa01有多个文件正在传送,此时,如果要关闭与aa01的聊天窗口,那么关闭之前,通常会先调用CancelFileTransferingAbout方法来取消与aa01相关的所有文件传送。所以你经常会看到类似的提示:“您与aa01有文件正在传送中,关闭当前窗口将导致正在传送的文件中断,您确定要关闭吗?”。如果用户确认关闭,此时就正是我们要调用CancelFileTransferingAbout方法的时候了。

(5)FileSendingEvents属性用于暴露自己作为发送者的所有正在进行的文件传送项目的实时状态;FileReceivingEvents属性用于暴露自己作为接收者的所有正在进行的文件传送项目的实时状态。

 

6.IFileHandler

      IFileHandler接口将被框架回调以实现文件传送机制。同IFileController一样,其既可用于客户端也可用户服务端。     

      我们需要实现ESPlus.Application.FileTransfering.IFileHandler接口来获取与文件传送请求相关通知: 

    public   interface   IFileBusinessHandler  IBusinessHandler
    {       
        
///   <summary>
        
///  是否同意接收文件?
         ///   </summary>         
        
///   <param name="senderID"> 发送者的ID。如果为NetServer.SystemUserID,则表示是服务端发送的。 </param>
        
///   <param name="fileName"> 文件名称。 </param>
        
///   <param name="fileLength"> 文件大小。 </param>
        
///   <param name="comment"> 其它附加备注。如果是在类似FTP的服务中,该参数可以是保存文件的路径 </param>
        
///   <param name="fileID"> 文件ID。 </param>
        
///   <param name="resumedFileItem"> 如果能续传,则不为null。 </param>
        
///   <returns> 返回值为保存文件的路径,如果为null,表示拒绝接收/拒绝续传文件。如果参数resumedFileItem不为null,而且返回路径等于resumedFileItem.LocalFileSavePath,则表示续传;否则表示另存。 </returns>
         string  ReadyToAcceptFile( string  senderID,  string  fileName,  long  fileLength,  string  comment,  string  fileID,  ResumedFileItem  resumedFileItem);          

        
///   <summary>
        
///  接收者对自己发送文件请求的回复 -- 同意/拒绝接收。
         ///   </summary>
        
///   <param name="info"> 文件传送项目的相关信息 </param>
        
///   <param name="agreed"> 对方是否同意 </param>
         void  OnResponseOfReceiver( TransmittingFileInfo  info , bool  agreed);
    }

(1)ReadyToAcceptFile方法是当前用户作为接收方时被框架回调的;而OnResponseOfReceiver方法是当前用户作为发送方时,被框架回调的。

(2)IFileHandler接口的两个方法都将在后台线程中被框架调用,如果实现该方法时需要刷新应用程序的UI,则注意一定要转发到UI线程。

(3)当发送方提交了发送文件的请求后,框架会在接收方回调ReadyToAcceptFileAsyn方法以询问是否同意接收,如果同意,ReadyToAcceptFileAsyn应返回有效的存储接收文件的路径,否则,返回null。

(4)当接收方同意或拒绝接收文件,框架会在发送方回调OnResponseOfReceiver方法以通知发送者。通常,应用程序在实现OnResponseOfReceiver方法时,最多只需要告知文件发送者,而不需要再做任何其它的额外处理。因为框架已经帮你打理好了一切。

      当接收方同意接收文件后,与该文件传送项目相关的事件会通过IFileOutter暴露的IFileTransferingEvents接口相继触发。

 

三.客户端

      同ESPlus的Basic应用或CustomizeInfo应用一样,在客户端支持文件传送功能也需要使用到相应的“Outter”组件和实现相应的“BusinessHandler”接口。

1.IFileOutter

      ESPlus.Application.FileTransfering.Passive.IFileOutter接口从IFileController继承,并增加了一个属性和一个方法:

    public   interface   IFileOutter  IOutter  , IFileController
    {
        
///   <summary>
        
///  发送文件数据包时所采用的消息优先级。
        
///   </summary>
         DataPriority  DataPriority4SendingFile {  get set ; }

        
///   <summary>
        
///  初始化文件传送查看器控件。
        
///   </summary>
        
///   <param name="viewer"> 文件传送查看器控件对象 </param>
        
///   <param name="destUserID"> 目标用户的ID。返回的查看器将显示与该用户相关的所有文件传送状态。如果传入null,则显示与任何用户的文件传送的实时状态。 </param>
         void  InitializeFileTransferingViewer( FileTransferingViewer  viewer,  string  destUserID);
    }

(1)我们可以通过设置DataPriority4SendingFile属性以控制发送文件数据包的优先级,在一般系统中,可以将其设置为Common或Low,但绝不能设置为CanBeDiscarded,否则将可能导致接收方接收到的文件不完整。   

(2)ESPlus提供了默认的传送项目的状态查看器控件FileTransferingViewer,如果没有特殊需求,大家在项目中可以直接使用它来显示文件传送的实时状态,它的界面截图如下所示:

     

      你只需要把这个控件拖拽到你的UI上,然后将其传入IFileOutter的InitializeFileTransferingViewer方法进行初始化后,它就会正常工作了。

      InitializeFileTransferingViewer方法的第二个参数destUserID表示当前的FileTransferingViewer控件要显示与哪个好友相关的所有文件传送项目的状态。以QQ作类比,你同时在与多个好友传送文件,那么就会有多个聊天窗口,每个聊天窗口都会有一个FileTransferingViewer实例,而这个FileTransferingViewer实例仅仅显示与当前聊天窗口对应的好友的传送项目。这样依赖,你与aa01用户传送文件的进度查看器就不会在你与aa02的聊天窗口上显示出来。

      如果你的FileTransferingViewer查看器需要捕捉所有正在传送的项目的实时状态,那么,调用InitializeFileTransferingViewer方法时,destUserID参数可以传入null。

      另外,FileTransferingViewer实现了IFileTransferingViewer接口:

    public   interface   IFileTransferingViewer
    {
        
///   <summary>
        
///  当某个文件开始续传时,触发该事件。参数为FileName - isSend
        
///   </summary>
         event   CbGeneric < string bool >  FileResumedTransStarted;

        
///   <summary>
        
///  当某个文件传送完毕时,触发该事件。参数为FileName - isSend
        
///   </summary>
         event   CbGeneric < string bool >  FileTransCompleted;

        
///   <summary>
        
///  当某个文件传送中断时,触发该事件。参数为FileName - isSend - FileTransDisrupttedType
        
///   </summary>
         event   CbGeneric < string bool , FileTransDisrupttedType >  FileTransDisruptted;

        
///   <summary>
        
///  当某个文件传送开始时,触发该事件。参数为FileName - isSend
        
///   </summary>
         event   CbGeneric < string bool >  FileTransStarted;

        
///   <summary>
        
///  当所有文件都传送完成时,触发该事件。
        
///   </summary>
         event   CbSimple  AllTaskFinished;

        
///   <summary>
        
///  当点击取消按钮终止某个文件传送时,触发该事件。
        
///   </summary>
         event   CbCancelFile  CancelFileButtonClicked; 

        
///   <summary>
        
///  当前是否有文件正在传送中。
        
///   </summary>         
         bool  IsFileTransfering();
    }

      你也可以通过该接口来关注FileTransferingViewer查看器捕捉到的(正如前所述,不一定是全部)文件传送项目的状态,而且,该接口的事件都是在UI线程中触发的,你可以直接在其处理函数中操控UI显示。

 

2.IFileBusinessHandler

      客户端的ESPlus.Application.FileTransfering.Passive.IFileBusinessHandler直接从IFileHandler继承,而且没有增加额外的内容:

    public   interface   IFileBusinessHandler  IBusinessHandler IFileHandler
    { 
    }

 

四.服务端 

      服务端也可以接收客户端发送的文件(即上传),甚至可以发送文件给客户端(即下载),它遵循同样的文件传送流程。

1.IFileTransferingController

      如果需要服务端也参与到文件的发送与接收中来,则同客户端一样,服务端的ESPlus.Application.FileTransfering.Server.IFileTransferingController接口也从IFileController继承,以提交文件发送请求、或取消正在发送中的文件等,这里就不重复解释了。

2.IFileBusinessHandler

   服务端需要实现ESPlus.Application.FileTransfering.Server.IFileBusinessHandler接口,也是直接从IFileHandler继承,而且没有增加额外的内容。

 

五.Rapid引擎对文件传送的支持

      新版本的Rapid引擎(可以从ESFramework 4.0 概述文末下载 ),增加了对文件传送的支持,使用Rapid引擎的朋友可以很方便的利用框架提供的文件传送功能。

1.客户端

      IRapidPassiveEngine的Initialize方法增加了一个重载:

   void  Initialize( string  userID,  string  serverIP,  int  serverPort,  IBasicBusinessHandler  basicHandler,  ICustomizeInfoBusinessHandler  customizeHandler , IFileBusinessHandler  fileHandler );         

      该重载增加了一个IFileBusinessHandler参数,用于支持文件传送机制。

      另外,IRapidPassiveEngine增加了一个IFileOutter只读属性,通过暴露的该属性,我们就可以提交发送文件的请求或监控文件传送的状态了。当然,如果在Initialize方法中没有传入有效的IFileBusinessHandler引用,则FileOutter属性将返回null。

 

2.服务端

      同客户端一样,服务端IRapidServerEngine的Initialize方法也增加了一个重载:

   void  Initialize( int  port,  ICustomizeInfoBusinessHandler  customizeInfoBusinessHandler, IFileBusinessHandler  fileBusinessHandler IFriendsManager  friendsManager,  IGroupManager  groupManager);

      IRapidServerEngine提供了IFileTransferingController只读属性,用于提交发送文件的请求或监控文件传送的状态。同样的,如果在Initialize方法中没有传入有效的IFileBusinessHandler引用,则FileTransferingController属性将返回null。

 

  Rapid引擎已经为我们组装好了与文件传送相关的所有组件,我们直接使用即可。如果后面有时间,我们会更深入地剖析ESPlus的文件传送机制背后的原理,以及如何一步步地搭建起文件传送的功能。

      ESPlus使得在基于ESFramework的通信系统中增加文件传送的功能变得如此简单,甚至,基于ESPlus提供的文件传送功能,我们可以很快地实现文件服务器,以支持文件上传和下载。

      接下来的一篇文章中,我们将在ESFramework 4.0 快速上手 -- 入门Demo,一个简单的IM系统(附源码)一文提供的demo的基础上增加文件传送的功能,作为示范,并提供新的demo源码下载,敬请期待!谢谢。 

 

ESFramework 4.0 概述

ESFramework 4.0 有哪些优点?

ESFramework 4.0 版本升级说明(持续更新)

《ESFramework 4.0 快速上手》系列所有文章

《ESFramework 4.0 高级进阶》系列所有文章 

 

 

目录
相关文章
|
5月前
|
开发框架 移动开发 小程序
强烈推荐:绝对是好用的小程序开源框架
强烈推荐:绝对是好用的小程序开源框架
132 0
|
4月前
|
开发框架 数据可视化 C#
C# | 上位机开发新手指南(三)框架
在上位机开发中,Windows Forms是使用最广泛的C#框架之一。Windows Forms是.NET Framework中的一个GUI框架,提供了丰富的GUI控件和易于使用的编程模型,可以快速开发Windows桌面应用程序。Windows Forms已经存在多年,并且在.NET Framework中得到广泛的支持和优化,因此在上位机开发中得到了广泛应用。除此之外,随着.NET Core的不断发展,越来越多的开发人员开始使用跨平台的C#框架进行上位机开发,例如使用Electron或Avalonia等框架开发基于Web技术的桌面应用程序。
168 0
C# | 上位机开发新手指南(三)框架
|
开发框架 弹性计算 JavaScript
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(下)
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(下)
303 0
|
存储 开发框架 缓存
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(中)
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(中)
200 0
|
Web App开发 开发框架 JSON
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(上)
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(上)
422 0
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(上)
|
移动开发 JavaScript 前端开发
强烈推荐:绝对是最好的一个小程序开源框架
小程序到底有多火,看看目前推出的开源框架以及组件库就知道了。由于小程序开发的火爆,大家都在致力于探索如何更好的,更加高效的开发小程序,以至于很多公司都贡献了小程序开源框架和组件库。
4891 0
|
JavaScript 中间件 API
从编程小白到全栈开发:基于框架开发服务端
上文中,我们了解了关于服务器端的一些概念知识,尤其是HTTP协议相关的最基本知识点,今天我想跟大家分享一下在平时正真的开发中,是如何来利用和体现这些内容的。
1267 0