最近工做中遇到了一个场景,要用C#客户端访问FTP服务器,并实现文件下载功能。以前我使用了一种很是简单粗暴的方法,由于客户端以前就用到了Xilium.CefGlue(能够理解为一个WebKit内核)来实现浏览网页的功能,客户的需求又仅停留在登陆FTP对部分压缩包和doc文件进行下载,我索性直接建了个页面,用这个WebKit内核实现对FTP进行访问,效果和Chrome浏览器访问FTP类似。html
不过,这个方法有下面三个缺点:sql
一、Xilium.CefGlue类库占用的空间很大,若是就为了实现客户端访问FTP服务器,放入一个WebKit内核,平白增长了几十MB的空间占用,是很是不划算的。数组
二、Xilium.CefGlue打开FTP相似Chrome的打开方式,遇到txt、sql等扩展名的文件时,会直接在浏览器中打开,遇到pdf扩展名的文件时,会使用相关插件打开(或因无相关处理工具而进入错误页)。遇到其余扩展名的文件时,如exe、rar、zip、doc等,才会提示下载。浏览器
三、没法知足许多用户定制化的需求(虽然内核是开源的,但你敢改么?)。服务器
因此说,使用C#客户端访问FTP服务器,最好的办法仍是本身写一套工具类,实现FTP协议下的上传、下载、建立目录、查询目录下文件列表等操做。工具
使用Serv-U工具能够在本机自建一个FTP服务,方法以下:测试
一、安装Serv-U并注册(试用版可使用30天,我用的版本是10.3.0.1)this
二、找到“新建域”按钮,新建一个FTP服务加密
三、新建域向导第一步:创建FTP域名,填写说明信息spa
四、新建域向导第二步:设置各协议端口号,通常来讲使用默认端口号便可
四、新建域向导第三步:也使用默认设置
五、新建域向导第四步:设置密码加密模式,选择“使用服务器设置”
六、Serv-U询问是否要创建用户,点击“是”便可
七、创建用户向导第一步:设置用户登陆ID为tsybius
八、创建用户向导第二步:设置密码,这里设置为123456
九、创建用户向导第三步:设置用户登陆FTP后看到的根目录
十、创建用户向导第四步:设置访问权限,有只读访问和彻底访问两种,这里我选择了彻底访问
十一、FTP创建完毕,在浏览器地址栏(或资源管理器地址栏)输入下面地址便可登陆FTP:
ftp://tsybius:123456@localhost/
通常来讲,使用C#程序访问FTP,只须要支持如下几个功能就足够了:
一、给定FTP下某一目录地址,获取该地址下全部的文件和目录及它们的详细信息
二、向FTP上传文件
三、从FTP下载文件
四、其余辅助功能(如刷新等)
咱们要实现的功能能够参考Xftp,即XShell打开的FTP访问工具
C#中调用FTP的方法是类似的,如获取指定目录下全部文件的详细信息能够写成:
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; namespace FTPManager { class FtpHelper { public static FtpFileInfo[] GetFtpFileInfos(string ftpPath, string userName, string passWord) { LinkedList<FtpFileInfo> linkedList = new LinkedList<FtpFileInfo>(); var reqFtp = (FtpWebRequest)WebRequest.Create(new Uri(ftpPath)); reqFtp.UsePassive = false; reqFtp.UseBinary = true; //reqFTP.EnableSsl = true;//加密方式传送数据 FTP 服务器要支持 reqFtp.Credentials = new NetworkCredential(userName, passWord); reqFtp.Method = WebRequestMethods.Ftp.ListDirectoryDetails; var response = (FtpWebResponse)reqFtp.GetResponse(); var reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8); string fileDetail = reader.ReadLine(); while (fileDetail != null) { linkedList.AddLast(new FtpFileInfo(fileDetail)); fileDetail = reader.ReadLine(); } reader.Close(); response.Close(); return linkedList.ToArray(); } } }
其中FtpFileInfo是我设计的一个用于管理FTP文件信息的类。下面贴出的代码只是一个很是简陋的版本,并无通过多少测试,不过可被看作一个解决问题的思路:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace FTPManager { public class FtpFileInfo { public string UnixFileType { get; set; } public string Permission { get; set; } public string NumberOfHardLinks { get; set; } public string Owner { get; set; } public string Group { get; set; } public string Size { get; set; } public string LastModifiedDate { get; set; } public string FileName { get; set; } public string FileDetail { get; set; } public FtpFileInfo(string fileDetail) { this.FileDetail = fileDetail; int counter = 1; string[] propertyBlocks = fileDetail.Split(' '); foreach (string propertyBlock in propertyBlocks) { switch (counter) { case 1: { //unix file types & permissions if (string.IsNullOrWhiteSpace(propertyBlock)) { continue; } else { if (propertyBlock.Length == 10) { UnixFileType = propertyBlock[0].ToString(); Permission = propertyBlock.Substring(1); } counter++; } } break; case 2: { //number of hard links if (string.IsNullOrWhiteSpace(propertyBlock)) { continue; } else { NumberOfHardLinks = propertyBlock; counter++; } } break; case 3: { //owner if (string.IsNullOrWhiteSpace(propertyBlock)) { continue; } else { Owner = propertyBlock; counter++; } } break; case 4: { //group if (string.IsNullOrWhiteSpace(propertyBlock)) { continue; } else { Group = propertyBlock; counter++; } } break; case 5: { //size if (string.IsNullOrWhiteSpace(propertyBlock)) { continue; } else { Size = propertyBlock; counter++; } } break; case 6: case 7: case 8: { //last-modified date if (string.IsNullOrWhiteSpace(propertyBlock)) { continue; } else { LastModifiedDate += propertyBlock + " "; counter++; } } break; case 9: { //file name if (string.IsNullOrWhiteSpace(propertyBlock)) { FileName += " "; } else { FileName += propertyBlock; } } break; } } LastModifiedDate = LastModifiedDate.Trim(); FileName = FileName.Trim(); } } }
根据reqFtp.Method的不一样,返回的流内容也会不一样,咱们须要对返回流的内容进行解析。reqFtp.Method一共支持如下几种枚举类型:
// 摘要: // 表示可与 FTP 请求一块儿使用的 FTP 协议方法的类型。没法继承此类。 public static class Ftp { // 摘要: // 表示要用于将文件追加到 FTP 服务器上的现有文件的 FTP APPE 协议方法。 public const string AppendFile = "APPE"; // // 摘要: // 表示要用于删除 FTP 服务器上的文件的 FTP DELE 协议方法。 public const string DeleteFile = "DELE"; // // 摘要: // 表示要用于从 FTP 服务器下载文件的 FTP RETR 协议方法。 public const string DownloadFile = "RETR"; // // 摘要: // 表示要用于从 FTP 服务器上的文件检索日期时间戳的 FTP MDTM 协议方法。 public const string GetDateTimestamp = "MDTM"; // // 摘要: // 表示要用于检索 FTP 服务器上的文件大小的 FTP SIZE 协议方法。 public const string GetFileSize = "SIZE"; // // 摘要: // 表示获取 FTP 服务器上的文件的简短列表的 FTP NLIST 协议方法。 public const string ListDirectory = "NLST"; // // 摘要: // 表示获取 FTP 服务器上的文件的详细列表的 FTP LIST 协议方法。 public const string ListDirectoryDetails = "LIST"; // // 摘要: // 表示在 FTP 服务器上建立目录的 FTP MKD 协议方法。 public const string MakeDirectory = "MKD"; // // 摘要: // 表示打印当前工做目录的名称的 FTP PWD 协议方法。 public const string PrintWorkingDirectory = "PWD"; // // 摘要: // 表示移除目录的 FTP RMD 协议方法。 public const string RemoveDirectory = "RMD"; // // 摘要: // 表示重命名目录的 FTP RENAME 协议方法。 public const string Rename = "RENAME"; // // 摘要: // 表示将文件上载到 FTP 服务器的 FTP STOR 协议方法。 public const string UploadFile = "STOR"; // // 摘要: // 表示将具备惟一名称的文件上载到 FTP 服务器的 FTP STOU 协议方法。 public const string UploadFileWithUniqueName = "STOU"; }
更详细的说明可参考MSDN页面:https://msdn.microsoft.com/zh-cn/library/ms144320.aspx
前面代码中使用到的是WebRequestMethods.Ftp.ListDirectoryDetails,所以返回流的内容为
返回的内容和Linux中命令“ls -l”是同样的,从左到右依次是:
Unix file types(Unix文件类型)、permissions(各用户权限)、number of hard links(硬链接数)、owner(全部者)、group(所属组)、size(文件大小)、last-modified date(文件最后更改时间)、filename(文件名)
关于Linux中ls命令的细节,能够参考维基百科页面:https://en.wikipedia.org/wiki/Ls
在主窗体下,写以下代码便可调用咱们刚才实现的FtpHelper.GetFtpFileInfos:
private void FormMain_Load(object sender, EventArgs e) { try { FtpFileInfo[] ftpFileInfos = FtpHelper.GetFtpFileInfos("ftp://localhost/", "tsybius", "123456"); dgvFileList.DataSource = ftpFileInfos; } catch (Exception ex) { MessageBox.Show(ex.Message); } }
(其中dgvFileList为一个DataGridView控件)
代码执行效果以下:
上面代码应注意之处有:
一、DataGridView的相关样式设定这里再也不赘述,我手动添加了四列,并为每列设置了DataPropertyName与FtpFileInfo字段相对应。
二、能够看出直接显示在DataGridView上的内容并不适合人阅读,文件类型、文件大小能够经过本身写两个继承自DataGridViewTextBoxColumn的类来实现使人舒服一些的显示。
三、上面的例子中,咱们把数组传入DataGridView的DataSource,这样作有一个弊端是不能点击各列列头对数据进行排序。若是但愿对数据排序,可将结果集转换成DataTable格式。
另附上我在网上找的几段C#访问FTP的代码,可供参考:
http://blog.csdn.net/chr23899/article/details/41787863
http://www.cnblogs.com/wang726zq/archive/2012/07/30/ftp.html
END