Question

I wrote the following code a long time ago to determine if an FTP directory exists:

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

        using (FtpWebResponse response = request.GetResponse() as FtpWebResponse)
        {
            StreamReader sr = new StreamReader(response.GetResponseStream(), System.Text.Encoding.ASCII);
            sr.ReadToEnd();
            sr.Close();
            response.Close();
        }
        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;
}

This code has worked for several years, but today it doesn't. When testing a directory that does not exist, the code in DirectoryExists() no longer throws an exception, and the method incorrectly returns true.

If I assign the results of sr.ReadToEnd() to a string, it is an empty string.

In this case, the code _host.GetUrl(filename) returned "ftp://www.mydomain.com/Articles/winforms/accessing-the-windows-registry". This is the expected value. And still my DirectoryExists() method does not throw an exception when this path does not exist on the server. I even passed this non-existing directory to a method that uses WebRequestMethods.Ftp.ListDirectoryDetails to build a directory listing. This method simply returns an empty listing and also throws no exception.

I believe I first encountered this issue when I moved my code to a new computer with Visual Studio 2013. I'm using .NET 4.5 and got the same behavior when using .NET 4.5.1.

Questions:

  1. Why doesn't this code, which has worked for years and uses the same technique used on most of the online examples I found, work? And what could possibly cause this code to suddenly stop working?

  2. Is there a way to detect for the presence of a directory that works? I suppose the other approach is to scan the parent directory, although the logic would need to be different when the routine is supposed to verify the root directory.

Was it helpful?

Solution

I managed to reproduce your error on another site I have access to. After some playing around, here's my conclusion:-

When you make a FtpWebRequest with a FTP URL that does NOT end with a /, such as:

ftp://ftp.someftp.com/somefolder/invalidfolder

AND you specified WebRequestMethods.Ftp.ListDirectory as the method, then what it does behind the scene is to run the following command:

NLST "somefolder/invalidfolder"

Normally, NLST will list the contents of the specified folder, and throws an exception if the folder does not exist. But because you did not specify a / at the end of invalidfolder, NLST will think that invalidfolder may actually be a file (or a filename pattern). If it manages to find a folder named invalidfolder, then and only then will it treat it as a folder. Otherwise it will try to search a file named invalidfolder underneath the parent folder somefolder. If the file does not exist, then one of the following will occur, depending on which FTP server software (and its configurations) is running:

  • It throws an ERROR 550: File or Folder not found. (Example: spftp v1.0)
  • It returns an empty result. (Example: vsFTPd v2.0.5)

In your case, the FTP server returns the latter response, and your code falls over.

The solution? Just add some validation code to make sure the ftp folder you are trying to access always has a / at the end. Something like the following:-

if (!directory.EndsWith('/'))
    directory += '/';

OTHER TIPS

In effect ftp class has strange beahvior however you should achieve your goal in this way using a simple console application project or simply in your original project too.

VBNET VERSION

    Sub Main()
      Dim ftp As New FtpSystem

      If ftp.DirectoryExist("p") Then
        Console.WriteLine("false")
      Else
        Console.WriteLine("true")
      End If
      Console.ReadLine()
    End Sub
    Private Class FtpSystem
    Public Function DirectoryExist(ByVal dir As String)
        Dim uri As New Uri("ftp://domain.com" & "/" & dir)
        Dim netCred As New NetworkCredential("user", "password")
        Dim ftprq As FtpWebRequest = FtpWebRequest.Create(uri)
        With ftprq
            .Credentials = netCred
            .KeepAlive = True
            .Method = WebRequestMethods.Ftp.ListDirectory
            .UsePassive = True
            .UseBinary = False
        End With
        Dim ftprs As FtpWebResponse = Nothing
        Dim Sr As StreamReader = Nothing
        'if ftp try to matching folder ad if it will succeed then return true if not it will thrown an exception 
        'and it will return false. Is not a good way because it should be implement a very granular exception matching but 
        'for test is best simple approach.
        Try
            ftprs = DirectCast(ftprq.GetResponse, FtpWebResponse)
            Sr = New StreamReader(ftprs.GetResponseStream)
            Dim T As String = Sr.ReadToEnd
            Console.Write(T)
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function
End Class

C# VERSION(TRANSLATE ON THE FLY WITH ONLINE TOOL PLEASE CHECK CODE)

   public void Main()
{
FtpSystem ftp = new FtpSystem();

if (ftp.DirectoryExist("p")) {
    Console.WriteLine("false");
} else {
    Console.WriteLine("true");
}
Console.ReadLine();
}
private class FtpSystem
{
public object DirectoryExist(string dir)
{
    Uri uri = new Uri("ftp://domain.com" + "/" + dir);
    NetworkCredential netCred = new NetworkCredential("user", "password");
    FtpWebRequest ftprq = FtpWebRequest.Create(uri);
    var _with1 = ftprq;
    _with1.Credentials = netCred;
    _with1.KeepAlive = true;
    _with1.Method = WebRequestMethods.Ftp.ListDirectory;
    _with1.UsePassive = true;
    _with1.UseBinary = false;
    FtpWebResponse ftprs = null;
    StreamReader Sr = null;
    //if ftp try to matching folder ad if it will succeed then return true if not it will thrown an exception 
    //and it will return false. Is not a good way because it should be implement a very granular exception matching but 
    //for test is best simple approach.
    try {
        ftprs = (FtpWebResponse)ftprq.GetResponse;
        Sr = new StreamReader(ftprs.GetResponseStream);
        string T = Sr.ReadToEnd;
        Console.Write(T);
        return true;
    } catch (Exception ex) {
        return false;
    }
    }
}

I hope that this help you.Bye

As mentionned above, perhaps the FTP server configuration changed. Have you tried to explicitly set the value of the UsePassive or UseBinary properties either ways ?

You can try sending ftp commands to the ftp server:

        ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");
        psi.UseShellExecute = false;
        psi.RedirectStandardOutput = true;
        psi.RedirectStandardInput = true;
        psi.RedirectStandardError = true;
        Process proc = Process.Start(psi);
        // Open the batch file for reading

        // Attach the output for reading
        StreamReader sOut = proc.StandardOutput;

        StreamReader sErr = proc.StandardError;    

        // Attach the in for writing
        StreamWriter sIn = proc.StandardInput;
        // Write each line of the batch file to standard input
        while (sr.Peek() != -1)
        {
            sIn.WriteLine("ftp");
            sIn.WriteLine("open remote-server-name");
            sIn.WriteLine("username");
            sIn.WriteLine("password");
            sIn.WriteLine("ls");
        }

        string outPutStuff= sOut.ReadToEnd();
        proc.WaitForExit();  //this does not appear to be needed. 
        string outErrStuff = sErr.ReadToEnd();

        Console.WriteLine("Start FileTransfer FTP Output");
        Console.WriteLine(outPutStuff);
        Console.WriteLine("Any errors follow this line---");
        Console.WriteLine(outErrStuff);

        Console.WriteLine(outPutStuff);

        sOut.Close();
        sIn.Close();

Please,check if there is a file named accessing-the-windows-registry (no file extension) in winforms folder.

_host.GetUrl(filename) should return a string ended with a "/". When there is file with the same name as the intended folder name, no exception will be thrown.

If you are not getting exceptions, as a possible work-around, you could try requesting a predictable attribute of the folder, like the date it was created.

Another suggestion is using WireShark to see what FTP requests are being made behind the scenes, which will let you see if it's .NET or the server returning unhelpful responses.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top