Как определить, существует ли каталог FTP

Я написал следующий код, чтобы определить, существует ли каталог. Метод DirectoryExists принимает либо полный, либо относительный путь.

public bool DirectoryExists(string directory)
{
    try
    {
        FtpWebRequest request = GetRequest(directory);
        request.Method = WebRequestMethods.Ftp.ListDirectory;

        using (FtpWebResponse response = request.GetResponse() as FtpWebResponse)
        using (StreamReader sr = new StreamReader(response.GetResponseStream(), System.Text.Encoding.ASCII))
        {
            sr.ReadToEnd();
        }
        return true;
    }
    catch { }
    return false;
}

protected FtpWebRequest GetRequest(string filename = "")
{
    FtpWebRequest request = WebRequest.Create(_host.GetUrl(filename)) as FtpWebRequest;
    request.Credentials = new NetworkCredential(Username, Password);
    request.Proxy = null;
    request.KeepAlive = false;
    return request;
}

Примечание: _host.GetUrl(filename) возвращает полный путь к указанному каталогу или имени файла. В моем случае это ftp://www.mydomain.com/Articles/controls/precisely-defining-kilobytes-megabytes-and-gigabytes.

Этот код проработал много месяцев. Но вдруг перестало работать. Теперь в DirectoryExists не возникает исключения, когда каталог не существует. sr.ReadToEnd() просто возвращает пустую строку.

Я отправил аналогичный вопрос, и мне было предложено всегда добавлять / в конец пути. Я пробовал это и думал, что однажды получил исключение, но сейчас это не вызывает исключения.

Я не знаю, изменилось ли поведение на FTP-сервере, с которым я общаюсь, или что. Опять же, это работало нормально, когда я его писал, а теперь нет.

Как я могу определить, существует ли каталог FTP?

РЕДАКТИРОВАТЬ:

Просматривая ответ после вызова ReadToEnd(), я вижу:

BannerMessage = 220 Служба Microsoft FTP \ r \ n

ContentLength = -1

ExitMessage = 221 До свидания. \ R \ n

StatusCode = ClosingData

StatusDescription = 226 Передача завершена. \ R \ n

WelcomeMessage = 230 Пользователь вошел в систему. \ R \ n

ОБНОВЛЕНИЕ:

В конце концов, я вижу, что большинство людей рекомендуют варианты того, что я делал изначально. Это придало вес предположению Ханса Пассанта о том, что проблема связана с сервером. Я пытаюсь заставить их взглянуть на это, но они кажутся немного сбитыми с толку всей дискуссией. Я знаю, что они используют сервер Microsoft, и сомневаюсь, что смогу получить от них решение.

Если все остальное терпит неудачу, я думаю, что ответ состоит в том, чтобы составить список родительского каталога, что требует некоторой дополнительной работы для обработки таких случаев, как, когда рассматриваемый каталог является корнем, а также случаев, когда родительский каталог не существует.


person Jonathan Wood    schedule 15.07.2014    source источник
comment
Может быть дубликатом stackoverflow.com/questions/2769137/   -  person Martin Vobr    schedule 31.07.2014
comment
Да, но мне кажется, что ни одно из этих предложений не работает.   -  person Jonathan Wood    schedule 31.07.2014
comment
Не разговаривайте с нами, поговорите с парнем, который администрирует сервер.   -  person Hans Passant    schedule 31.07.2014
comment
@HansPassant: Почему? Я не контролирую все FTP-серверы в мире. Ищу надежный способ работы с любым FTP-сервером.   -  person Jonathan Wood    schedule 31.07.2014
comment
На надежность можно рассчитывать только тогда, когда надежен FTP-сервер. Только он, конечно, может исправить этот сервер, чтобы он снова вел себя правильно. Если вы полагаетесь на другую машину для обеспечения надежной работы вашего программного обеспечения, очень важно, чтобы вы узнали, кто управляет этой машиной.   -  person Hans Passant    schedule 31.07.2014
comment
Метод ListDirectory может быть ненадежным на некоторых серверах. Возможно, комбинации команд MLSD, CWD, MDTM и SIZE FTP будут работать лучше, как описано в: stackoverflow.com/a/4472637/16132   -  person Martin Vobr    schedule 31.07.2014
comment
С каким программным обеспечением FTP-сервера вы подключаетесь? Может, есть известный глюк или обходной путь?   -  person Martin Vobr    schedule 31.07.2014
comment
@MartinVobr: Цель здесь - написать код, который работает с любым FTP-сервером. Мне неинтересно писать код, который работает только с одним конкретным сервером.   -  person Jonathan Wood    schedule 31.07.2014
comment
@Jonathan Wood: Я столкнулся с FTP-сервером, который требовал задержки в одну секунду между отправкой имени пользователя и пароля. Вы бы замедлили свой код для каждого FTP-сервера или бы вы добавили код для замедления только при обнаружении некорректного сервера? Существует также удобный, быстрый и стандартизированный способ получения списка каталогов FTP (расширение MLSD). К сожалению, он не поддерживается некоторыми FTP-серверами. Альтернатива - угадать, какой формат листинга удобен для чтения, и попытаться разобрать его (часто неоднозначно). Будете ли вы использовать лучший метод, когда он доступен, или вместо него использовать устаревший?   -  person Martin Vobr    schedule 01.08.2014


Ответы (5)


FTP-серверы отвечают сообщением 550, когда вы запрашиваете несуществующий каталог / файл. Этот 550 преобразован в исключение в .NET.

Что касается представленного вами кода, я не вижу причин использовать StreamReader в производстве. Простая модификация следующая:

public bool DirectoryExists(string directory)
{
  try
  {
    FtpWebRequest request = GetRequest(directory);
    request.Method = WebRequestMethods.Ftp.ListDirectory;
    return request.GetResponse() != null;
  }
  catch
  {
    return false;
  }
}

Я сохранил обработку исключений как есть (отсутствует), но я предлагаю вам поработать над этим, поскольку есть много разных случаев, которые нужно уловить, которые не связаны с ответами ftp.

request.GetResponse () сгенерирует исключение, если каталог не существует.

Я пробовал использовать следующие способы получения истинных результатов:

Последний - существующий, но пустой каталог.

ftp://ftp.mozilla.org/pub/data/bloat-reports2/ возвращает false

Есть большое но, касающееся замыкающих / входящих путей.

mozilla.org - это файл с нулевой длиной байта в каталоге pub

ftp://ftp.mozilla.org/pub/mozilla.org возвращает значение true

ftp://ftp.mozilla.org/pub/mozilla.org/ вернул false а тут правда ?????

Такое поведение похоже на то, что вы описали. Вы можете легко воспроизвести его в браузере. Я предлагаю вам проделать то же самое со своими путями и проверить содержимое каталогов. Если у вас есть пустые файлы, удалите их или замените их содержимое пробелом и, наконец, проверьте свой код, чтобы убедиться, что вы не будете создавать новые файлы или воссоздавать их с нулевой длиной байта.

Я надеюсь, что это помогает.

ОБНОВЛЕНИЕ

Нет новостей, плохие новости! Я предполагаю, что ничего из вышеперечисленного вам не помогло. Может быть, поднявшись на один уровень выше, вы решите проблему. Идея состоит в том, чтобы получить список родительского каталога и проверить его на предмет дочернего имени. Если родительский путь действителен, он всегда должен возвращать непустую строку. Код следующий:

static bool DirectoryExists(string directory)
{
    try
    {
        directory = GetRequest(directory);
        string
            parent = directory.Substring(0, directory.TrimEnd('/').LastIndexOf('/') + 1),
            child = directory.Substring(directory.TrimEnd('/').LastIndexOf('/') + 1).TrimEnd('/');
        FtpWebRequest request = GetRequest(parent);
        request.Method = WebRequestMethods.Ftp.ListDirectory;
        using (FtpWebResponse response = request.GetResponse() as FtpWebResponse)
        {
            if (response == null)
                return false;
            string data = new StreamReader(response.GetResponseStream(), true).ReadToEnd();
            return data.IndexOf(child, StringComparison.InvariantCultureIgnoreCase) >= 0;
        }
    }
    catch
    {
        return false;
    }
}

Как видите, не нужно беспокоиться о косых чертах в конце.

Предупреждение: код не будет охватывать случай подкаталога и имени файла с одинаковыми именами по одному и тому же пути.

person Community    schedule 31.07.2014
comment
Хотя ваш первый блок кода, кажется, содержит некоторые улучшения, в основном он делает то же самое, что и мой код. А именно, он полагается на исключение, создаваемое при вызове WebRequestMethods.Ftp.ListDirectory для несуществующего каталога. К сожалению, результат тоже такой же. В моем случае исключение не создается. Кроме того, результатом GetResponse() является пустая строка, а не null. - person Jonathan Wood; 04.08.2014
comment
Что касается вашего второго предложения, я над ним подумал. Конечно, такой подход не работает, когда данный каталог является корневым. Я могу попробовать что-то подобное, если не смогу найти другое решение. Я связался с людьми, которые размещают сервер, чтобы узнать, не происходит ли там чего-нибудь странного. - person Jonathan Wood; 04.08.2014
comment
Первый подход относится к случаю файлов с нулевой длиной байта как к потенциальной причине. Отсутствие новостей было сигналом для второй. Вы правы насчет корневого каталога, но это не связано с тем, как вы организовываете свои пути (/ article / controls / ...). Рассматривайте это как прямой обходной путь для вашего конкретного случая, а не как глобально предлагаемое решение. - person ; 04.08.2014
comment
Я думаю, что это самый полезный ответ, и я награжу его наградой. Я обновил свой вопрос, чтобы показать свое общее впечатление о наилучшем подходе. - person Jonathan Wood; 06.08.2014

Это могло произойти из-за того, что на вашем сервере установлено программное обеспечение FTP-сервера.

В противном случае вы можете взглянуть на response.StatusCode, чтобы увидеть, есть ли какие-либо подсказки.

http://msdn.microsoft.com/en-us/library/system.net.ftpstatuscode(v=vs.110).aspx

Наконец, если все это не работает, попробуйте записать тупой текстовый файл в этот несуществующий каталог.

person Hiệp Lê    schedule 31.07.2014
comment
Я обновил свой вопрос, чтобы показать, каковы некоторые значения свойств response, включая StatusCode. - person Jonathan Wood; 03.08.2014

У меня был аналогичный код, который развертывал элементы через FTP на IIS 6 / Windows 2003 Server. Это работало нормально, пока я не указал на сервер IIS7 / Windows 2008. Я начал наблюдать точно такое же поведение, как и вы. Я отладил его и в конечном итоге изменил свой код на следующий. Это прямой фрагмент моего кода. Я могу включить источник из методов, которые он вызывает, если хотите, но я думаю, вы понимаете эти операции.

try
{
    //Get a recursive FTP Listing
    items = GetFtpListing(target, true);
}
catch(WebException e)
{
    if (string.Equals(e.Message, "The remote server returned an error: (550) File unavailable (e.g., file not found, no access)."))
    {
        CreateRemoteDirectory(target);
        items = GetFtpListing(target, true);
    }
    else
    {
        throw e;
    }
}

соответствующий раздел GetFtpListing

List<string> ftpRawList = new List<string>();

FtpWebRequest request = (FtpWebRequest)WebRequest.Create(path);
request.Timeout = 60000;
request.ReadWriteTimeout = 60000;
request.Credentials = this.credentials;
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
    Stream data = response.GetResponseStream();

    using (StreamReader reader = new StreamReader(data))
    {
        string responseString = reader.ReadToEnd();
        ftpRawList.AddRange(responseString.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries));
    }
person mckeejm    schedule 01.08.2014
comment
Я не уверен, что вижу здесь исправление. По сути, похоже, что вы используете WebRequestMethods.Ftp.ListDirectoryDetails вместо WebRequestMethods.Ftp.ListDirectory. Я попробовал это и получил те же результаты. Я не понимаю, в какой части этого была устранена проблема, с которой вы столкнулись. - person Jonathan Wood; 04.08.2014
comment
if (string.Equals(e.Message, "The remote server returned an error: (550) File unavailable (e.g., file not found, no access).")) { CreateRemoteDirectory(target); items = GetFtpListing(target, true); } - это то, что исправлено, в частности проверка на 550 и создание каталога, если он не существует (поскольку в моей модели я создаю структуру, в которую мы загружаем файлы. - person mckeejm; 05.08.2014
comment
Хорошо спасибо. Но я вижу в этом две неотложные проблемы. Во-первых, вы сравниваете строку в обработчике исключений, и, как я уже отмечал, я не получаю никаких исключений. И, во-вторых, я считаю подозрительным тестировать всю эту строку, которая может легко измениться в будущем. По крайней мере, вы должны проанализировать его, чтобы найти номер ошибки, поскольку я считаю, что .NET уже имеет свойства, которые делают именно это. - person Jonathan Wood; 05.08.2014
comment
да, возможно, re: конкретная строка. Учитывая, что мы выполняем развертывание на известном целевом сервере, который вряд ли изменится, я не беспокоюсь об этом для моего использования, но согласен с тем, что если бы это использовалось другими клиентами, мне бы потребовалось лучшее решение. Извините, я пропустил то место, где вы не получали исключения ... есть ли что-нибудь в вашем ответе? или это просто ноль? - person mckeejm; 05.08.2014
comment
Как упоминалось в моем вопросе, я возвращаю пустую строку. Видимо, это то же самое, если директор существует, но пуст. - person Jonathan Wood; 06.08.2014
comment
Я только что более внимательно изучил ваш код, и похоже, что вы можете установить для KeepAlive значение true http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.keepalive(v=vs.110).aspx. Я просто смотрю, что изменилось ... это не имеет значения ... но я заметил разницу - person mckeejm; 06.08.2014
comment
Не знаю, должно ли это иметь значение, но, похоже, это не так. :) - person Jonathan Wood; 06.08.2014

Я использую приведенное ниже, чтобы проверить, действительны ли учетные данные для ftp-подключения. Вы можете использовать то же самое, чтобы проверить, существует ли каталог. Возвращает истину, если URL-адрес, имя пользователя и пароль верны.

URL: здесь вы указываете путь к вашему каталогу. пользователь: ftp имя пользователя пароль: ftp пароль


public static bool isValidConnection(string url, string user, string password, ref string errorMessage = "")
{
    try {
        FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
        request.Method = WebRequestMethods.Ftp.ListDirectory;
        request.Credentials = new NetworkCredential(user, password);
        request.KeepAlive = false;
        request.UsePassive = true;
        FtpWebResponse response = (FtpWebResponse)request.GetResponse();
        response.Close();
    } catch (WebException ex) {
        errorMessage = ex.Message;
        return false;
    }
    return true;
}
person Sagar Rao    schedule 04.08.2014
comment
Вы используете WebRequestMethods.Ftp.ListDirectory. Это именно то, что я использовал. - person Jonathan Wood; 04.08.2014

Один из способов - перечислить содержимое каталога, присутствующее во многих решениях, но в моем случае мне также нужно читать содержимое данных, чтобы сервер проверял каталог. Если каталог отсутствует, создается исключение webException, которое следует интерпретировать для фильтрации непредвиденных ошибок.

Вот мое решение:

  bool directoryExists(string path)
    {

        bool? exists = null;


        FtpWebRequest request = (FtpWebRequest)WebRequest.Create(path);
        request.Method = WebRequestMethods.Ftp.ListDirectory;

        request.Credentials = new NetworkCredential("username", "*****");

        FtpWebResponse response = null;
        try
        {
            response = (FtpWebResponse)request.GetResponse();

            using (StreamReader sr = new StreamReader(response.GetResponseStream()))
            {
                string str = sr.ReadLine(); //Just needs to read one line to fire check on server
                sr.Close();
                return true;
            }                                    
        }
        catch (WebException ex)  
        {
            var r = (FtpWebResponse)ex.Response;
            if (r.StatusCode ==
                FtpStatusCode.ActionNotTakenFileUnavailable)
            {
                //Does not exist
                return false;
            }
            throw; //if error is not related to directory existence then the exception needs to be treated above.
        }
        finally
        {
            if (response != null)
                response.Close();
        }
        return false;
    }

Я знаю, что FTP-сервер, на который я нацелен, является сервером Windows, но у меня нет доступа к информации о версии.

person MiguelSlv    schedule 18.04.2017