ESFramework Demo -- 文件传送Demo(附源码)
现在我们将在ESFramework Demo -- 入门Demo,简单的即时通讯系统 的基础上,使用ESPlus提供的第三个武器,为其增加文件传送的功能。在阅读本文之前,请务必先掌握ESFramework 开发手册(03) -- 文件(夹)传送 一文中介绍的文件传送的流程及相关的API的用法。
本文的demo仅仅实现了客户端与客户端之间的文件传送,至于传送文件夹,以及服务器与客户端之间的文件传送则采用完全一样的模型,大家可以在本demo的基础上自行扩展。
本Demo演示以下与文件传送相关的特性:
(1)发送方请求发送文件,接收方可以同意或拒绝接收文件。
(2)文件传送的过程中,收发的任何一方都可以通过事件了解文件传送的实时进度。
(3)文件传送的过程中,收发的任何一方都可以中断文件的传送。
(4)文件传送的过程中,收发的任何一方掉线,都将导致文件传送中断。
(5)只要文件传送中断,收发方都会得到相应的事件通知。
(6)自动启用文件断点续传。
(7)文件传送完成,收发方都会得到相应的事件通知。
一.服务端
由于在demo中,服务端不参与文件传送,所以,服务端的代码不用做任何修改,直接使用上一个demo中的服务端即可。
顺便提一下,如果想让服务端作为文件收发的一方,也很容易,只要遵循以下几点:
(1)使用IRapidServerEngine暴露的FileController属性,来控制文件的收发行为。
(2)预定IFileController的FileSendingEvents事件和FileReceivingEvents事件,来跟踪文件传送的实时状态。
(3)服务端的虚拟帐号为NetServer.SystemUserID,即"_0"。当一个文件的接收者的UserID为NetServer.SystemUserID,表示文件是由服务端接收的;当一个文件的发送者的UserID为NetServer.SystemUserID,表示这个文件是由服务端发送的。
二.客户端
相对于上一个demo,客户端改动的地方主要集中在MainForm和ChatForm上。客户端使用IRapidPassiveEngine暴露的FileOutter属性,来控制文件的收发行为。
发送与接收流程实现
首先,ChatForm的最上方增加了一个“发送文件”的按钮,点击此按钮的时候,将选择文件,并请求发送。此时,客户端作为发送方的身份出现。
//请求发送文件
private void toolStripButton1_Click(object sender, EventArgs e)
{
string filePath = ESBasic.Helpers.FileHelper.GetFileToOpen("打开");
if (filePath == null)
{
return;
}
string fileID;
SendingFileParas sendingFileParas = new SendingFileParas(20480, 0);//文件数据包大小,可以根据网络状况设定,局网内可以设为204800,传输速度可以达到30M/s以上;公网建议设定为2048或4096或8192
this.fileOutter.BeginSendFile(this.friendID, filePath, null, sendingFileParas, out fileID);
}
其次,客户端在MainForm的Initialize方法中通过预定IRapidPassiveEngine的FileOutter的FileRequestReceived事件,来获得接收到了文件传送请求的通知。此时,客户端是作为接收方的身份出现。
//预定收到了来自发送方发送文件(夹)的请求的事件
this.rapidPassiveEngine.FileOutter.FileRequestReceived += new CbFileRequestReceived(fileOutter_FileRequestReceived);
注意,FileRequestReceived事件有个ResumedProjectItem类型的参数,用于表示当前传送项目是否能够续传。如果该参数不为null,则表示可以续传;如果该参数为null,则表示是一个全新的传送项目,不能续传。
在该事件的处理函数中,最终会弹出MessageBox,询问用户是否同意接收,如果同意,则选择保存路径。根据用户的操作,我们需要将是否同意接收文件的答案通知给发送方。即ChatForm的OnFileRequest方法:
//当接收到对方的文件传送请求时
public void OnFileRequest(string fileID, string senderID, string fileName)
{
if (DialogResult.OK == MessageBox.Show(string.Format("{0}要求向你传输文件{1},你是否同意接收?", senderID, fileName), "文件传输", MessageBoxButtons.OKCancel))
{
string savePath = ESBasic.Helpers.FileHelper.GetPathToSave("保存", fileName, null);
if (!string.IsNullOrEmpty(savePath))
{
this.fileOutter.BeginReceiveFile(fileID, savePath);
}
else
{
this.RejectFile(fileID, fileName);
}
}
else
{
this.RejectFile(fileID, fileName);
}
}
private void RejectFile(string fileID, string fileName)
{
TransferingProject fileInfo = this.fileOutter.GetTransferingProject(fileID);
if (fileInfo != null)
{
this.textChatControl1.ShowRejectFile(fileName);
this.fileOutter.RejectFile(fileID);
}
}
发送方又是在MainForm的Initialize方法中通过预定IRapidPassiveEngine的FileOutter的FileResponseReceived事件,来得到对方的回复的:
//预定接收方回复了同意/拒绝接收文件(夹)时的事件
this.rapidPassiveEngine.FileOutter.FileResponseReceived += new CbGeneric<TransferingProject, bool>(fileOutter_FileResponseReceived);
如果对方同意接收,则ESPlus会在后台自动启动文件发送线程进行文件发送。
客户端无论是作为发送方还是接收方,都在ChatForm中使用了FileTransferingViewer控件来显示各个文件传送项目的实时状态,并且通过预定其状态变化事件来接收通知,然后在聊天窗口中显示相应的信息。
Demo运行起来后,传送文件的截图如下所示:
当关闭聊天窗口ChatForm时,判断当前是否有文件正在传输。如果有,则提醒用户,如果用户仍然决定关闭,则在关闭窗体之前先要取消相关的正在传送的项目。
private void ChatForm_FormClosing(object sender, FormClosingEventArgs e)
{
List<string> fileIDs = this.fileOutter.GetTransferingAbout(this.friendID);
if (fileIDs != null && fileIDs.Count > 0)
{
DialogResult result = MessageBox.Show("如果关闭窗口,就会中止文件传送。是否关闭窗口?", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.None);
if (result == DialogResult.OK)
{
this.fileOutter.CancelTransferingAbout(this.friendID);
}
else
{
e.Cancel = true;
}
}
}
断点续传
当文件传送中断(比如因为掉线)后,发送方再次发送该文件,此时如果接收方同意接收,并且保存的路径与上次完全一致,那么框架底层将自动启用断点续传。 发送方和接收方以预定了FileTransferingViewer的FileResumedTransStarted事件,来得知断点续传启动的信息。
更简单的,就像前面提到的,FileRequestReceived事件的ResumedProjectItem类型的参数,如果其值不为null,则表示可以续传。如果用户选择了续传,则就直接使用ResumedProjectItem的LocalSavePath属性的值作为文件存储路径,去调用IFileOutter的BeginReceiveFile方法就可以了。有兴趣的朋友,可以按照这种模式修改一下demo代码,就可以完全达到类似QQ的续传效果。
三.FileTransferingViewer控件
FileTransferingViewer控件用于显示每个文件传送项目的实时状态。与QQ的文件传送的UI相比,FileTransferingViewer只是一个用于demo的简单控件。我们这里将FileTransferingViewer的源码开放给大家,大家可以在其基础上进行修改,以达到自己想要的效果(或与QQ完全一样的效果)。
众多其它细节,已经在ESFramework 开发手册(03) -- 文件(夹)传送 一文中作了详细介绍,这里不再赘述。大家可以参考上文和本文,然后对照源码进行研究,很容易就可以理解内部的运转流程了。
四.源码下载
(1)ESFramework.Demos.FileTransfer 源码
(2)ESFramework.Boosts 源码:ESFramework 增强库,里面有本文用到的FileTransferingViewer控件的源码
-----------------------------------------------------------------------------------------------------------------------------------------------
Q Q:168757008