专题十一:实现一个基于FTP协议的程序——文件上传下载器

引言:

   在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信大家追MM都有自己的经验的,我感觉大部分的过程肯定是——第一步: 先通过工作关系或者朋友关系等认识MM(认识FTP协议,知道FTP协议的是什么) ; 第二步: 当然了解MM有兴趣爱好了(了解FTP协议有哪些命令和工作过程)第三步:如果对方是你的菜的话,那当然要采取追求的了(就好比用了解到的FTP协议来实现一个文件上传下载器)。不过追MM好像对我来说还是比较难的了, 所以还是言归正传了,还是好好的学习我的代码吧,回到本章的内容——FTP的协议。

  (注:最近想好好改进下文章的幽默程度,所以文章中会尽量以有趣的生活中的例子来表述网络编程的知识,希望可以让大家在学习知识的同时也可以获得乐趣,如果有什么地方理解不准确的还望大家多多指出。)

一、FTP协议的自我介绍

  我们在上学的时候,同学们第一次开学的时候老师一般会组织大学到讲台上进行自我介绍,让同学都互相认识,同样,如果对于没有接触过FTP协议的朋友来说,FTP协议的自我介绍当然也是不可避免的了,这样我们才能进一步去了解FTP协议 “这位同学”了,之后才能和他成为好朋友,或者是好基友了。下面就开始FTP协议同学的自我介绍了, 大家热烈欢迎。

  FTP 协议同学: 大家好, 我的名字叫FTP,FTP是文件传输协议(File Transfer Protocol,FTP),我的工作就是负责将文件从一台计算机传输到另外一台计算机,并且我还可以保证传输的可靠性。我的工作流程可以通过下面的一张图来表达:

          

  从图中大家应该可以明白我的工作过程了吧,我的工作过程是典型的C/S模式——我的客户端(在本章实现的文件上传下载器属于客户端)首先发起与我的服务器连接连接,告诉我的服务器说“我现在想和你聊聊天”, 然后我的服务器收到这个请求后给出回答——“聊天,当然可以了,我批准了”,客户端收到这个信息后,就可以服务器之间就建立一条马路或者是通道,然后我的客户端好还想进一步了解下我的服务器,在发出一个说“我想要下载你上面的东西 或者是 我想上传一些文件到你那里,想让你帮我保管下,这样我可以随处都可以从你那里得到我上传的资料的”, 我的服务器收到请求后,如果允许客户端这么做的话就会回答说 “可以啊”(就像我们追女生一样,建立好关系后,当然就要表白了,此时我们就说“我很喜欢你之类的话”,然后等待MM的回答,“可以啊” 这个答案都是我们希望听到的答案的),我的客户端听到后非常开心,马上选择自己需要上传的文件或者想从服务器下载的文件找到,上传或者下载该文件的。 我还要补充一点,在访问我的FTP服务器之前必须登录,这样我的服务器才认识你,才可能会搭理你的,登录时就需要客户端提供一个用户名和密码,提供了正确的用户名和密码后就可以和我的服务器进行聊天 和请求上传或下载我服务器上的文件了; 然而我的某些服务器提供了一种匿名的方式,我的客户端不需要提供用户名和密码就可以进行聊天了,其实匿名的方式和我聊天的本质是:提供服务的公司或机构在我的服务器上建立一个公用的账户,方便那些没有提供用户名和密码的客户端与我聊天。 上面就是我的自我介绍了,谢谢大家。

 二、.Net 为实现我的客户端提供了些什么?

  可以说微软真是一位雷锋叔叔的,因为在他的.Net类库中提供了很多类库供我们使用,当然为实现我的客户端也提供了一些类的支持的, 现在就看看这位好人帮我们提供了哪些类来对实现一个FTP客户端程序的支持的。

  这位好人通过命名空间System.Net下的FtpWebRequest类和FtpWebResponse类提供对实现FTP客户端的支持。

2.1 FtpWebRequest类

  该类是WebRequest类的派生类,FTPWebRequest类用于向服务器发出请求,告诉服务器说“我想和你聊天",如果要获得FtpWebRequest的一个实例,则需要使用Create方法来创建实例,对于该类如何使用我在这里也就不一一列出来的, 大家可以查看MSDN的相关文档来了解方法的使用,并且在本专题实现的程序中也会有所介绍的,下面给出MSDN中的一个链接的:http://msdn.microsoft.com/zh-cn/library/8exfzxft.aspx  

2.2 FtpWebResponse类

  FTP客户端既然发话了,服务器当然也要有所表示的了, 不要哑巴一样不说话的,总要给个答复的,FtpWebResponse类就负责封装FTP服务器对客户端请求的回答的一个类。FTP客户端通过GetResponse方法来获得FtpWebResponse类的对象的,如果服务器回答说“我们可以聊天的”,这样就说明他们俩就可以互相沟通了,就好比追MM的时候你问MM说“可以给电话号码给我吗?”,然后MM对你也有好感就告诉你一个号码后,得到MM的号码也就和MM建立了沟通的通道了,就好比服务器回答“我们可以聊天的”。之后客户端和服务器就可以进行进一步的沟通(上传文件到服务器或者要求服务器给些文件给客户端),之后的过程就好比你可以通过电话号码和MM进一步的交流,知道MM的有些什么性格和爱好的。下面提供一个MSDN中该类的使用链接,这里我就不一一介绍他的成员了,大家可以到MSDN中查看的,上面每个属性和方法都有一个比较好的解释,并且大家也可以通过下面实现的FTP客户端程序进一步了解该类的使用:http://msdn.microsoft.com/zh-cn/library/system.net.ftpwebresponse.aspx

三、如何实现一个FTP客户端程序?——看完下面的介绍你就会知道了

通过FTP协议的自我介绍部分大家应该可以明白了FTP协议的工作过程的,然而一个FTP客户端程序就是基于FTP协议的文件上传下载器,通过这个程序大家可以对FTP服务器上的资料进行浏览、上传和下载等操作的。

程序中主要模块的代码:

登录模块:

  1 #region 登录模块的实现
  2         // 登录服务器事件
  3         private void btnlogin_Click(object sender, EventArgs e)
  4         {
  5             if (tbxServerIp.Text == string.Empty)
  6             {
  7                 MessageBox.Show("请先填写服务器IP地址", "提示");
  8                 return;
  9             }
 10 
 11             ftpUristring = "ftp://" + tbxServerIp.Text;
 12             networkCredential = new NetworkCredential(tbxUsername.Text, tbxPassword.Text);
 13             if (ShowFtpFileAndDirectory() == true)
 14             {
 15                 btnlogin.Enabled = false;
 16                 btnlogout.Enabled = true;
 17                 lstbxFtpResources.Enabled = true;
 18                 lstbxFtpState.Enabled = true;
 19                 tbxServerIp.Enabled = false;
 20                 if (chkbxAnonymous.Checked == false)
 21                 {
 22                     tbxUsername.Enabled = false;
 23                     tbxPassword.Enabled = false;
 24                     chkbxAnonymous.Enabled = false;
 25                 }
 26                 else
 27                 {
 28                     chkbxAnonymous.Enabled = false;
 29                 }
 30 
 31                 tbxloginmessage.Text = "登录成功";
 32                 btnUpload.Enabled = true;
 33                 btndownload.Enabled = true;
 34                 btnDelete.Enabled = true;
 35             }
 36             else
 37             {
 38                 lstbxFtpState.Enabled = true;
 39                 tbxloginmessage.Text = "登录失败";
 40             }
 41         }
 42 
 43         // 显示资源列表
 44         private bool ShowFtpFileAndDirectory()
 45         {
 46             lstbxFtpResources.Items.Clear();
 47             string uri = string.Empty;
 48             if (currentDir == "/")
 49             {
 50                 uri = ftpUristring;
 51             }
 52             else
 53             {
 54                 uri = ftpUristring + currentDir;
 55             }
 56 
 57             string[] urifield = uri.Split(' ');
 58             uri = urifield[0];
 59             FtpWebRequest request = CreateFtpWebRequest(uri, WebRequestMethods.Ftp.ListDirectoryDetails);
 60             
 61             // 获得服务器返回的响应信息
 62             FtpWebResponse response = GetFtpResponse(request);
 63             if (response == null)
 64             {
 65                 return false;
 66             }
 67             lstbxFtpState.Items.Add("连接成功,服务器返回的是:"+response.StatusCode+" "+response.StatusDescription);
 68 
 69             // 读取网络流数据
 70             Stream stream = response.GetResponseStream();
 71             StreamReader streamReader = new StreamReader(stream,Encoding.Default);
 72             lstbxFtpState.Items.Add("获取响应流....");
 73             string s = streamReader.ReadToEnd();
 74             streamReader.Close();
 75             stream.Close();
 76             response.Close();
 77             lstbxFtpState.Items.Add("传输完成");
 78             
 79             // 处理并显示文件目录列表
 80             string[] ftpdir = s.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
 81             lstbxFtpResources.Items.Add("↑返回上层目录");
 82             int length = 0;
 83             for (int i = 0; i < ftpdir.Length; i++)
 84             {
 85                 if (ftpdir[i].EndsWith("."))
 86                 {
 87                     length = ftpdir[i].Length - 2;
 88                     break;
 89                 }
 90             }
 91 
 92             for (int i = 0; i < ftpdir.Length; i++)
 93             {
 94                 s = ftpdir[i];
 95                 int index = s.LastIndexOf('\t');
 96                 if (index == -1)
 97                 {
 98                     if (length < s.Length)
 99                     {
100                         index = length;
101                     }
102                     else
103                     {
104                         continue;
105                     }
106                 }
107 
108                 string name = s.Substring(index + 1);
109                 if (name == "." || name == "..")
110                 {
111                     continue;
112                 }
113 
114                 // 判断是否为目录,在名称前加"目录"来表示
115                 if (s[0] == 'd' || (s.ToLower()).Contains("<dir>"))
116                 {
117                     string[] namefield = name.Split(' ');
118                     int namefieldlength = namefield.Length;
119                     string dirname;                   
120                     dirname = namefield[namefieldlength - 1];
121 
122                     // 对齐
123                     dirname = dirname.PadRight(34,' ');
124                     name = dirname;
125                     // 显示目录
126                     lstbxFtpResources.Items.Add("[目录]" + name);
127                 }
128             }
129 
130             for (int i = 0; i < ftpdir.Length; i++)
131             {
132                 s = ftpdir[i];
133                 int index = s.LastIndexOf('\t');
134                 if (index == -1)
135                 {
136                     if (length < s.Length)
137                     {
138                         index = length;
139                     }
140                     else
141                     {
142                         continue;
143                     }
144                 }
145 
146                 string name = s.Substring(index + 1);
147                 if (name == "." || name == "..")
148                 {
149                     continue;
150                 }
151 
152                 // 判断是否为文件
153                 if (!(s[0] == 'd' || (s.ToLower()).Contains("<dir>")))
154                 {
155                     string[] namefield = name.Split(' ');
156                     int namefieldlength = namefield.Length;
157                     string filename;
158                  
159                     filename = namefield[namefieldlength - 1];
160                    
161                     // 对齐
162                     filename = filename.PadRight(34, ' ');           
163                     name = filename;
164 
165                     // 显示文件
166                     lstbxFtpResources.Items.Add(name);
167                 }
168             }
169 
170             return true;
171         }
172 
173         // 注销事件
174         private void btnlogout_Click(object sender, EventArgs e)
175         {
176             btnlogin.Enabled = true;
177             btnlogout.Enabled = false;
178             tbxServerIp.Enabled = true;
179             tbxServerIp.SelectAll();
180             tbxServerIp.Focus();
181             chkbxAnonymous.Enabled = true;
182             if (chkbxAnonymous.Checked == false)
183             {
184                 tbxUsername.Enabled = true;
185                 tbxPassword.Enabled = true;
186             }
187 
188             tbxloginmessage.Text = "你已经退出了。";
189             lstbxFtpResources.Items.Clear();
190             lstbxFtpResources.Enabled = false;
191             lstbxFtpState.Items.Clear();
192             lstbxFtpState.Enabled = false;
193             btnUpload.Enabled = false;
194             btndownload.Enabled = false;
195             btnDelete.Enabled = false;
196         }
197 
198         #endregion
View Code

对FTP服务器操作模块(本程序中实现下载、上传和删除的功能):

  1 #region 对文件的操作模块实现
  2         // 上传文件到服务器事件
  3         private void btnUpload_Click(object sender, EventArgs e)
  4         {
  5             // 选择要上传的文件
  6             OpenFileDialog openFileDialog = new OpenFileDialog();
  7             openFileDialog.FileName = openFileDialog.FileNames.ToString();
  8             openFileDialog.Filter = "所有文件(*.*)|*.*";
  9             if (openFileDialog.ShowDialog() != DialogResult.OK)
 10             {
 11                 return;
 12             }
 13 
 14             FileInfo fileinfo = new FileInfo(openFileDialog.FileName);
 15             try
 16             {
 17                 string uri = GetUriString(fileinfo.Name);
 18                 FtpWebRequest request = CreateFtpWebRequest(uri, WebRequestMethods.Ftp.UploadFile);
 19                 request.ContentLength = fileinfo.Length;
 20                 int buflength = 8196;
 21                 byte[] buffer = new byte[buflength];
 22                 FileStream filestream = fileinfo.OpenRead();
 23                 Stream responseStream = request.GetRequestStream();
 24                 lstbxFtpState.Items.Add("打开上传流,文件上传中...");
 25                 int contenlength = filestream.Read(buffer, 0, buflength);
 26                 while (contenlength != 0)
 27                 {
 28                     responseStream.Write(buffer, 0, contenlength);
 29                     contenlength = filestream.Read(buffer, 0, buflength);
 30                 }
 31 
 32                 responseStream.Close();
 33                 filestream.Close();
 34                 FtpWebResponse response = GetFtpResponse(request);
 35                 if (response == null)
 36                 {
 37                     lstbxFtpState.Items.Add("服务器未响应...");
 38                     lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
 39                     return;
 40                 }
 41 
 42                 lstbxFtpState.Items.Add("上传完毕,服务器返回:" + response.StatusCode + " " + response.StatusDescription);
 43                 lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
 44                 MessageBox.Show("上传成功!");
 45 
 46                 // 上传成功后,立即刷新服务器目录列表
 47                 ShowFtpFileAndDirectory();
 48             }
 49             catch (WebException ex)
 50             {
 51                 lstbxFtpState.Items.Add("上传发生错误,返回信息为:" + ex.Status);
 52                 lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
 53                 MessageBox.Show(ex.Message, "上传失败");
 54             }
 55         }
 56 
 57 
 58         private string GetUriString(string filename)
 59         {
 60             string uri = string.Empty;
 61             if (currentDir.EndsWith("/"))
 62             {
 63                 uri = ftpUristring + currentDir + filename;
 64             }
 65             else
 66             {
 67                 uri = ftpUristring + currentDir + "/" + filename;
 68             }
 69 
 70             return uri;
 71         }
 72 
 73         // 从服务器上下载文件到本地事件
 74         private void btndownload_Click(object sender, EventArgs e)
 75         {
 76             string fileName = GetSelectedFile();
 77             if (fileName.Length == 0)
 78             {
 79                 MessageBox.Show("请选择要下载的文件!","提示");
 80                 return;
 81             }
 82 
 83             // 选择保存文件的位置
 84             SaveFileDialog saveFileDialog = new SaveFileDialog();
 85             saveFileDialog.FileName = fileName;
 86             saveFileDialog.Filter = "所有文件(*.*)|(*.*)";
 87             if (saveFileDialog.ShowDialog() != DialogResult.OK)
 88             {
 89                 return;
 90             }
 91 
 92             string filePath = saveFileDialog.FileName;
 93             try
 94             {
 95                 string uri = GetUriString(fileName);
 96                 FtpWebRequest request = CreateFtpWebRequest(uri, WebRequestMethods.Ftp.DownloadFile);
 97                 FtpWebResponse response = GetFtpResponse(request);
 98                 if (response == null)
 99                 {
100                     lstbxFtpState.Items.Add("服务器未响应...");
101                     lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
102                     return;
103                 }
104 
105                 Stream responseStream = response.GetResponseStream();
106                 FileStream filestream = File.Create(filePath);
107                 int buflength = 8196;
108                 byte[] buffer = new byte[buflength];
109                 int bytesRead =1;
110                 lstbxFtpState.Items.Add("打开下载通道,文件下载中...");
111                 while (bytesRead != 0)
112                 {
113                     bytesRead = responseStream.Read(buffer, 0, buflength);
114                     filestream.Write(buffer, 0, bytesRead);
115                 }
116 
117                 responseStream.Close();
118                 filestream.Close();
119                 lstbxFtpState.Items.Add("下载完毕,服务器返回:" + response.StatusCode + " " + response.StatusDescription);
120                 lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
121                 MessageBox.Show("下载完成!");
122             }
123             catch (WebException ex)
124             {
125                 lstbxFtpState.Items.Add("发生错误,返回状态为:" + ex.Status);
126                 lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
127                 MessageBox.Show(ex.Message, "下载失败");
128             }
129         }
130 
131         // 获得选择的文件
132         // 如果选择的是目录或者是返回上层目录,则返回null
133         private string GetSelectedFile()
134         {
135             string filename = string.Empty;
136             if (!(lstbxFtpResources.SelectedIndex == -1 || lstbxFtpResources.SelectedItem.ToString().Substring(0, 4) == "[目录]"))
137             {
138                 string[] namefield = lstbxFtpResources.SelectedItem.ToString().Split(' ');
139                 filename = namefield[0];
140             }
141             return filename;
142 
143         }
144         // 删除服务器文件事件
145         private void btnDelete_Click(object sender, EventArgs e)
146         {
147             string filename = GetSelectedFile();
148             if (filename.Length == 0)
149             {
150                 MessageBox.Show("请选择要删除的文件!", "提示");
151                 return;
152             }
153 
154             try
155             {
156                 string uri = GetUriString(filename);
157                 if (MessageBox.Show("确定要删除文件 " + filename + " 吗?", "确认文件删除", MessageBoxButtons.YesNo) == DialogResult.Yes)
158                 {
159                     FtpWebRequest request = CreateFtpWebRequest(uri, WebRequestMethods.Ftp.DeleteFile);
160                     FtpWebResponse response = GetFtpResponse(request);
161                     if (response == null)
162                     {
163                         lstbxFtpState.Items.Add("服务器未响应...");
164                         lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
165                         return;
166                     }
167 
168                     lstbxFtpState.Items.Add("文件删除成功,服务器返回:" + response.StatusCode + " " + response.StatusDescription);
169                     ShowFtpFileAndDirectory();
170                 }
171                 else
172                 {
173                     return;
174                 }
175             }
176             catch (WebException ex)
177             {
178 
179                 lstbxFtpState.Items.Add("发生错误,返回状态为:" + ex.Status);
180                 lstbxFtpState.TopIndex = lstbxFtpState.Items.Count - 1;
181                 MessageBox.Show(ex.Message, "删除失败");
182             }
183         }
184         #endregion
View Code

  由于程序的演示效果需要结合下一专题介绍的FTP服务器,具体的演示效果大家可以查看——专题十二:实现一个简单的FTP服务器,下面就列出程序的主界面截图:

 四、小结

   这个专题的介绍就到这里的,在下一个专题将和大家介绍下如何实现一个FTP服务器,这样再加上本专题制作的FTP文件上传下载器就可以形成一个完整的软件套件,自己实现FTP文件上传下载器访问自己实现的FTP服务器将会让大家觉得很很有趣的, 想赶快体验下这样的一种乐趣吗?那就赶快下载本专题的源码来亲身体验下吧。通过希望通过本专题让大家对FTP协议不再陌生,并且做Asp.net开发的朋友,文件的上传和下载是一个公共模块的,然后Asp.net中的文件上传和下载只是通过浏览器向HTTP服务器发送HTTP命令,来告诉HTTP服务器说“我想和你对话”,“我想要你上面的某某文件”以及“我想上传一个文件到你的上面去”等等的对话,这个系列完成之后,我也会和大家总结下网络编程的知识的。  

最后提供下源码下载地址:https://files.cnblogs.com/zhili/FTPUpDownloader.zip

转自:http://www.cnblogs.com/zhili/archive/2012/10/18/FTPClient.html

 

posted @ 2016-02-25 14:27  WKellyL  阅读(3132)  评论(0编辑  收藏  举报