天天看点

如何实现离线文件?1.客户端发送离线文件2.服务端接收离线文件      3.服务端发送离线文件给最终接收者

      近段时间,有几个朋友问我如何实现类似QQ离线文件的功能。不想一一作答,就写一篇博文来比较完整的解释这个问题。

      下面我们就用户在使用离线文件时,按各个动作发生的先后顺序,介绍程序方面与之对应的设计与实现。

      当用户选择好一个文件,并点击“发送离线文件”按钮时,其目的是要将这个文件传送给服务端,这可以直接使用IFileOutter的BeginSendFile方法:

      如果将参数accepterID传入null,表示文件的接收者就是服务端。那么我们要如何区分,这不是一个最终由服务端接收的文件,而是要传给另一个用户的离线文件了?这里,我们可以巧用comment参数,比如,comment参数如果为null,就表示普通的上传文件;comment不为null,就表示一个离线文件,并且其值就是文件最终接收者的ID。(当然,如果在你的项目中,comment参数已经有了其它用途,我们可以进一步扩展它,加上一些标签,使其能够标志出离线文件)。

      下面这个调用示例,就是将Test.txt文件离线发送给aa01。

      客户端调用BeginSendFile方法请求发送文件后,服务端会触发IFileController的FileRequestReceived事件。同理,我们判断该事件的comment参数,当其不为null时,表示是个离线文件。在答复客户端同意接收文件之前,我们需要先将离线文件的相关信息保存起来,这里我们使用OfflineFileItem类来封装这些信息。

        有了OfflineFileItem的定义之后,我们就可以处理IFileController的FileRequestReceived事件了。 

      上面的代码做了三件事情:

(1)根据某种策略得到存放文件的路径。

(2)创建一个离线文件信息条目,保存在内存中。

(3)回复客户端,并准备接收文件。

    需要重点说明的是第一点,对于一般的小型项目,在服务端我们可以将所有的离线文件存放在当前服务器的某个目录下;但是对于大型项目,一般需要使用DFS(分布式文件系统)来存储这些临时的离线文件。

    客户端收到服务器的回复后,会正式开始传送文件,如果传送过程中,因为某种原因导致传送中断,则服务端会触发IFileController.FileReceivingEvents的FileTransDisruptted事件。在该事件处理函数中,我们从内存中移除对应的离线文件信息条目: 

      如果文件正常传送完毕,则服务端会触发IFileController.FileReceivingEvents的FileTransCompleted事件。此时,我们将对应的离线文件信息条目从内存转移存储到数据库中,以防止服务器重启时导致信息丢失: 

       我们设计IOfflineFilePersister接口,用于与数据库中的OfflineFileItem表交互。 

       我们可以使用ADO.NET或者EntityFramework实现上述接口。

      当真正的接收者上线时,服务端要把相关的离线文件发送给他。通过预定UserManager的SomeOneConnected事件,我们知道用户上线的时刻。 

(1)从数据库中查找所有接收者为登录用户的离线文件信息条目。

(2)将离线文件逐个发送给这个用户

(3)从数据库中删除相应的条目,从磁盘上删除对应的离线文件。

      实际上,第(3)点我们可以延迟到文件发送完成时,才执行删除操作。这样,就可以在发送万一意外中断时,使得重新发送成为可能。

      客户端接收到服务端的发送文件请求时,会触发IFileOutter的FileRequestReceived事件,此时也可以根据comment参数的内容,来判断其是否为离线文件。后续的步骤的实现就相当容易了,这里就不再赘述了。

      本文简洁地描述了实现离线文件功能的主要思路和基本模型,在实际的项目开发时,可以根据具体的需求在这个模型的基础上,进一步完善,包括很多细节和异常处理都需要加入进来。