在很多分布式系统中,都有文件传送的需求。ESPlus内置了文件传送(支持自动断点续传)的功能,通过ESPlus.Application.FileTransfering命名空间提供相关服务。最新的ESPlus不仅支持传送单个文件,还支持传送整个文件夹,而且,传送文件夹与传送文件采用完全相同的API和模型。
(1) 由发送方发起传送文件的请求。
(2) 接收方回复同意或者拒绝接收文件。如果拒收,则流程结束;否则进入下一步。
(3) 发送方发送文件数据,接收方接收文件数据。
(4) 如果文件传送过程中,接收方或发送方掉线或者取消文件传送,则文件传送被中断,流程结束。如果文件传送过程一直正常,则到最后完成文件的传送。
有几点需要说明一下:
(1) 发送方可以是客户端,也可以是服务器;接收方也是如此。但无论发送方和接收方的类别如何,它们都遵守这一文件传送流程;就像ESFramework所有的通信引擎都公用同一套消息处理骨架流程一样。
(2) 当接收方同意接收后,框架会自动搜索是否存在匹配的续传项目,若存在,则会启动断点续传。当然,我们可以通过文件接收管理器的属性来控制断点续传功能是否开启。
(3) 进行文件传送的线程是由框架自动控制的,只要发送方收到了接收方同意接收的回复,框架就会自动在后台线程中发送文件数据包;同样,此时接收方也会自动处理接收到的文件数据包。
(4) 发送方或接收方都可随时取消正在传送的文件。
(5) 当文件传送被中断或完成时,发送方和接收方都会有相应的事件通知。
无论是发送方还是接收方,针对每个文件传送任务,都有一个对象来表示它,ESPlus.FileTransceiver.TransferingProject便是一个文件传送项目的封装,里面包含了类似发送者ID、接收者ID、文件名称、是否已经开始传送,等等相关信息。
TransferingProject的大部分属性对于发送方和接收方都是有效的,而有几个属性只对发送方有效(比如SendingFileParas),有几个属性只对接收方有效(如LocalSaveFilePath),这些在帮助文档中都有详细的说明。而且,有些属性(如OriginFileLastUpdateTime)的存在是用于支持断点续传功能的。
ESPlus使用FileTransDisrupttedType枚举定义了所有可能导致文件传送中断的原因:
当文件传送通过P2P通道进行时,如果P2P通道意外中断,则文件传送也会中断,其中断的原因就是枚举中的ReliableP2PChannelClosed。ESPlus定义了IFileTransferingEvents接口,用于暴露所有与文件传送相关的状态和事件:
通过预定这些事件,我们可以知道每个传送的文件什么时候开始(或断点续传)、什么时候完成、传递的实时进度、传送中断的原因等等。要注意的是,这些事件都是在后台线程中触发的,如果在事件处理函数中需要更新UI,则需要将调用转发到UI线程。
该对象仅仅包含两个属性:SendingSpanInMSecs和FilePackageSize。发送方可以通过SendingFileParas对象来指定发送文件数据包时的频率与每个数据包的大小。一般来说,为了达到最快的传送速度,SendingSpanInMSecs可以设为0。而FilePackageSize的大小则要根据发送方与接收方的网络环境的好坏进行决定,在Internet上,一般可以设为2048或4096左右;而在局网内,可以设为204800甚至更大(在局网的传送速度可以达到30M/s以上)。
通过ESPlus.Application.FileTransfering.IBaseFileController接口,我们可以提交发送文件的请求,并且可以主动取消正在接收或发送的文件。IBaseFileController即可用于客户端也可用户服务端。
同ESPlus的Basic应用或CustomizeInfo应用一样,在客户端支持文件传送功能需要使用到相应的“Outter”组件IFileOutter。
客户端通过ESPlus.Application.FileTransfering.Passive.IFileOutter接口提供的方法来提交发送文件请求等操作。我们可以从ESPlus.Rapid.IRapidPassiveEngine暴露的FileOutter属性来获取IFileOutter引用。
IFileOutter接口直接从IBaseFileController继承,且未增加任何新的内容:
ESPlus提供了默认的传送项目的状态查看器控件ESPlus.Widgets.FileTransferingViewer,如果没有特殊需求,大家在项目中可以直接使用它来显示文件传送的实时状态,它的界面截图如下所示:
你只需要把这个控件拖拽到你的UI上,然后将IFileOutter传入FileTransferingViewer的Initialize方法,它就可以正常工作了。
FileTransferingViewer的Initialize方法的第一个参数friendUserID表示当前的FileTransferingViewer控件要显示与哪个好友相关的所有文件传送项目的状态。以QQ作类比,你同时在与多个好友传送文件,那么就会有多个聊天窗口,每个聊天窗口都会有一个FileTransferingViewer实例,而这个FileTransferingViewer实例仅仅显示与当前聊天窗口对应的好友的传送项目。这样依赖,你与aa01用户传送文件的进度查看器就不会在你与aa02的聊天窗口上显示出来。
如果你的FileTransferingViewer需要捕捉所有正在传送的项目的实时状态,那么,调用其Initialize方法时,friendUserID参数传入null就可以了。另外,FileTransferingViewer实现了IFileTransferingViewer接口:
你也可以通过该接口来关注FileTransferingViewer查看器捕捉到的(正如前所述,不一定是全部)文件传送项目的状态,而且,该接口的事件都是在UI线程中触发的,你可以直接在其处理函数中操控UI显示。 你也可以通过该接口来关注FileTransferingViewer查看器捕捉到的(正如前所述,不一定是全部)文件传送项目的状态,而且,该接口的事件都是在UI线程中触发的,你可以直接在其处理函数中操控UI显示。
服务端也可以接收客户端发送的文件(即上传),甚至可以发送文件给客户端(即下载),它遵循同样的文件传送流程。如果需要服务端也参与到文件的发送与接收中来,可以使用服务端的ESPlus.Application.FileTransfering.Server. IFileController接口,直接从IBaseFileController接口继承,而且未增加任何新的内容。我们可以从ESPlus.Rapid.IRapidServerEngine暴露的FileController属性来获取IFileController引用。
最后,大家可以查看文件传送demo的源码,并运行demo,来了解文件(夹)的传送流程和API的使用。我们后面会专门撰文介绍该demo,谢谢。
关于ESFramework的任何问题,欢迎联系我们:
电话:027-87638960
Q Q:372841921