Ответ 1
FtpWebRequest
не имеет явной поддержки для рекурсивных операций с файлами (включая загрузку). Вы должны сами реализовать рекурсию:
- Список удаленного каталога
- Итерировать записи, загружать файлы и рекурсировать в подкаталоги (перечислять их снова и т.д.).
Трудная часть состоит в том, чтобы идентифицировать файлы из подкаталогов. Нет способа сделать это переносимым способом с помощью FtpWebRequest
. К сожалению, FtpWebRequest
не поддерживает команду MLSD
, которая является единственным переносимым способом получения списка каталогов с атрибутами файлов в протоколе FTP. См. Также Проверка, является ли объект на FTP-сервере файлом или каталогом.
Ваши варианты:
- Сделайте операцию над именем файла, которое наверняка потерпит неудачу для файла и будет успешным для каталогов (или наоборот). То есть вы можете попробовать загрузить "имя". Если это удастся, это файл, если это не удается, это каталог.
- Вам может быть повезло, и в вашем конкретном случае вы можете указать файл из каталога по имени файла (т.е. все ваши файлы имеют расширение, а в подкаталогах нет).
- Вы используете длинный список каталогов (
LIST
command =ListDirectoryDetails
method) и попытайтесь проанализировать список, относящийся к серверу. Многие FTP-серверы используют список * nix-style, где вы идентифицируете каталог с помощьюd
в самом начале записи. Но многие серверы используют другой формат. Следующий пример использует этот подход (при условии, что формат * nix)
void DownloadFtpDirectory(string url, NetworkCredential credentials, string localPath)
{
FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
listRequest.Credentials = credentials;
List<string> lines = new List<string>();
using (FtpWebResponse listResponse = (FtpWebResponse)listRequest.GetResponse())
using (Stream listStream = listResponse.GetResponseStream())
using (StreamReader listReader = new StreamReader(listStream))
{
while (!listReader.EndOfStream)
{
lines.Add(listReader.ReadLine());
}
}
foreach (string line in lines)
{
string[] tokens =
line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
string name = tokens[8];
string permissions = tokens[0];
string localFilePath = Path.Combine(localPath, name);
string fileUrl = url + name;
if (permissions[0] == 'd')
{
if (!Directory.Exists(localFilePath))
{
Directory.CreateDirectory(localFilePath);
}
DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
}
else
{
FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadRequest.Credentials = credentials;
using (FtpWebResponse downloadResponse =
(FtpWebResponse)downloadRequest.GetResponse())
using (Stream sourceStream = downloadResponse.GetResponseStream())
using (Stream targetStream = File.Create(localFilePath))
{
byte[] buffer = new byte[10240];
int read;
while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
targetStream.Write(buffer, 0, read);
}
}
}
}
}
Используйте следующую функцию:
NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/download/";
DownloadFtpDirectory(url, credentials, @"C:\target\directory");
Если вы хотите избежать проблем с разбором форматов списков каталогов для конкретного сервера, используйте стороннюю библиотеку, которая поддерживает команду MLSD
и/или разбор различных форматов списков LIST
; и рекурсивные загрузки.
Например, сборка WinSCP.NET вы можете загрузить целую директорию с помощью одного вызова Session.GetFiles
:
// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp.example.com",
UserName = "user",
Password = "mypassword",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Download files
session.GetFiles("/directory/to/download/*", @"C:\target\directory\*").Check();
}
Внутри WinSCP использует команду MLSD
, если она поддерживается сервером. Если нет, он использует команду LIST
и поддерживает десятки различных форматов листинга.
Метод Session.GetFiles
рекурсивный по умолчанию.
(Я автор WinSCP)