在没有任何外部库的情况下实现这一点并不容易。不幸的是,.NET Framework 和 PowerShell 都没有明确支持在 FTP 目录中递归列出文件。
你必须自己实现:
- 列出远程目录
- 迭代条目,递归到子目录 - 再次列出它们,等等。
棘手的部分是从子目录中识别文件。使用 .NET Framework (FtpWebRequest) 无法以可移植的方式做到这一点。不幸的是,.NET Framework 不支持MLSD 命令,这是在 FTP 协议中检索具有文件属性的目录列表的唯一可移植方式。另见Checking if object on FTP server is file or directory。
您的选择是:
- 对文件名执行操作,对于文件肯定会失败,而对于目录会成功(反之亦然)。 IE。您可以尝试下载“名称”。
- 您可能很幸运,在您的特定情况下,您可以通过文件名来区分目录中的文件(即,您的所有文件都有扩展名,而子目录没有)
- 您使用长目录列表(
LIST 命令 = ListDirectoryDetails 方法)并尝试解析特定于服务器的列表。许多 FTP 服务器使用 *nix 样式的列表,您可以在其中通过条目开头的 d 标识目录。但是许多服务器使用不同的格式。以下示例使用这种方法(假设为 *nix 格式)
static void ListFtpDirectory(string url, NetworkCredential credentials)
{
WebRequest listRequest = WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (WebResponse listResponse = listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (StreamReader listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
string line = listReader.ReadLine();
lines.Add(line);
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
if (permissions[0] == 'd')
{
Console.WriteLine($"Directory {name}");
string fileUrl = url + name;
ListFtpDirectory(fileUrl + "/", credentials);
}
else
{
Console.WriteLine($"File {name}");
}
}
}
使用如下函数:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/list/";
ListFtpDirectory(url, credentials);
如果您想避免解析服务器特定目录列表格式的麻烦,请使用支持MLSD 命令和/或解析各种LIST 列表格式的第三方库。
例如使用WinSCP .NET assembly,您可以通过调用Session.EnumerateRemoteFiles 递归地列出整个目录:
// Setup session options
var sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (var session = new Session())
{
// Connect
session.Open(sessionOptions);
// Enumerate files
var options =
EnumerationOptions.EnumerateDirectories | EnumerationOptions.AllDirectories;
IEnumerable<RemoteFileInfo> fileInfos =
session.EnumerateRemoteFiles("/directory/to/list", null, options);
foreach (var fileInfo in fileInfos)
{
Console.WriteLine(fileInfo.FullName);
}
}
不仅代码更简单、更健壮且独立于平台。它还使所有其他文件属性(大小、修改时间、权限、所有权)都可以通过RemoteFileInfo class 轻松获得。
如果服务器支持,WinSCP 在内部使用MLSD 命令。如果没有,它使用LIST 命令并支持数十种不同的列表格式。
(我是 WinSCP 的作者)