确定FTP目录是否存在的代码突然停止工作

时间:2021-11-15 05:46:39

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

很久以前,我编写了以下代码,以确定一个FTP目录是否存在:

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.

这段代码已经工作了好几年,但今天却没有。当测试不存在的目录时,DirectoryExists()中的代码不再抛出异常,并且方法错误地返回true。

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

如果我将sr.ReadToEnd()的结果赋给一个字符串,它就是一个空字符串。

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.

在本例中,代码_host.GetUrl(文件名)返回“ftp://www.mydomain.com/articles/winforms/accessing-thewindowsregistry”。这是期望值。而且,我的DirectoryExists()方法不会在服务器上不存在此路径时抛出异常。我甚至将这个不存在的目录传递给了一个使用WebRequestMethods.Ftp的方法。ListDirectoryDetails创建目录列表。此方法只返回一个空清单,并且不会抛出异常。

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.

我相信我第一次遇到这个问题是在我将代码移动到带有Visual Studio 2013的一台新电脑上的时候。我使用。net 4.5,在使用。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.

    是否有一种方法可以检测是否存在一个有效的目录?我认为另一种方法是扫描父目录,尽管当例程要验证根目录时,逻辑需要有所不同。

6 个解决方案

#1


5  

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:

当您创建一个FtpWebRequest时,它的FTP URL不以/结尾,例如:

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:

你指定的WebRequestMethods.Ftp。ListDirectory作为方法,它在场景后面执行以下命令:

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:

通常,NLST将列出指定文件夹的内容,如果该文件夹不存在,则抛出异常。但是因为您没有在invalidfolder的末尾指定一个/,NLST会认为invalidfolder实际上可能是一个文件(或文件名模式)。如果它设法找到一个名为invalidfolder的文件夹,那么只有这样,它才会把它当作文件夹。否则,它将尝试在父文件夹somefolder下搜索名为invalidfolder的文件。如果该文件不存在,则将发生以下情况之一,具体取决于哪个FTP服务器软件(及其配置)正在运行:

  • It throws an ERROR 550: File or Folder not found. (Example: spftp v1.0)
  • 它抛出一个错误550:文件或文件夹没有找到。(例如:spftp v1.0)
  • It returns an empty result. (Example: vsFTPd v2.0.5)
  • 它返回一个空结果。(例如:vsFTPd v2.0.5)

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

在您的例子中,FTP服务器返回后一个响应,您的代码就会崩溃。

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:-

解决方案?只需添加一些验证代码,以确保正在尝试访问的ftp文件夹最后总是有一个/。类似以下:-

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

#2


1  

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.

实际上,ftp类具有奇怪的beahvior,但是您应该使用简单的控制台应用程序项目或简单的原始项目来实现您的目标。

VBNET VERSION

VBNET版本

    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)

c#版本(在线翻译工具,请检查代码)

   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

希望这对你有帮助,再见

#3


0  

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 ?

如上所述,可能FTP服务器配置发生了更改。您是否试图以任何方式显式地设置UsePassive属性或UseBinary属性的值?

#4


0  

You can try sending ftp commands to the ftp server:

您可以尝试向ftp服务器发送ftp命令:

        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();

#5


0  

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

请检查winforms文件夹中是否有一个名为access -windows-registry(无文件扩展名)的文件。

_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.

geturl(文件名)应该返回以“/”结尾的字符串。当有与预期的文件夹名称同名的文件时,不会抛出异常。

#6


0  

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.

另一个建议是使用WireShark来查看在幕后正在发出什么FTP请求,这将让您看到它是. net还是返回无用响应的服务器。

#1


5  

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:

当您创建一个FtpWebRequest时,它的FTP URL不以/结尾,例如:

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:

你指定的WebRequestMethods.Ftp。ListDirectory作为方法,它在场景后面执行以下命令:

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:

通常,NLST将列出指定文件夹的内容,如果该文件夹不存在,则抛出异常。但是因为您没有在invalidfolder的末尾指定一个/,NLST会认为invalidfolder实际上可能是一个文件(或文件名模式)。如果它设法找到一个名为invalidfolder的文件夹,那么只有这样,它才会把它当作文件夹。否则,它将尝试在父文件夹somefolder下搜索名为invalidfolder的文件。如果该文件不存在,则将发生以下情况之一,具体取决于哪个FTP服务器软件(及其配置)正在运行:

  • It throws an ERROR 550: File or Folder not found. (Example: spftp v1.0)
  • 它抛出一个错误550:文件或文件夹没有找到。(例如:spftp v1.0)
  • It returns an empty result. (Example: vsFTPd v2.0.5)
  • 它返回一个空结果。(例如:vsFTPd v2.0.5)

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

在您的例子中,FTP服务器返回后一个响应,您的代码就会崩溃。

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:-

解决方案?只需添加一些验证代码,以确保正在尝试访问的ftp文件夹最后总是有一个/。类似以下:-

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

#2


1  

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.

实际上,ftp类具有奇怪的beahvior,但是您应该使用简单的控制台应用程序项目或简单的原始项目来实现您的目标。

VBNET VERSION

VBNET版本

    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)

c#版本(在线翻译工具,请检查代码)

   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

希望这对你有帮助,再见

#3


0  

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 ?

如上所述,可能FTP服务器配置发生了更改。您是否试图以任何方式显式地设置UsePassive属性或UseBinary属性的值?

#4


0  

You can try sending ftp commands to the ftp server:

您可以尝试向ftp服务器发送ftp命令:

        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();

#5


0  

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

请检查winforms文件夹中是否有一个名为access -windows-registry(无文件扩展名)的文件。

_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.

geturl(文件名)应该返回以“/”结尾的字符串。当有与预期的文件夹名称同名的文件时,不会抛出异常。

#6


0  

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.

另一个建议是使用WireShark来查看在幕后正在发出什么FTP请求,这将让您看到它是. net还是返回无用响应的服务器。