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

时间:2014-04-21 20:02:23

标签: c# .net ftp ftpwebrequest

我很久以前写过以下代码来确定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;
}

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

如果我将sr.ReadToEnd()的结果分配给字符串,则为空字符串。

在这种情况下,代码_host.GetUrl(filename)返回“ftp://www.mydomain.com/Articles/winforms/accessing-the-windows-registry”。这是预期值。当服务器上不存在此路径时,我的DirectoryExists()方法仍然不会抛出异常。我甚至将这个不存在的目录传递给使用WebRequestMethods.Ftp.ListDirectoryDetails构建目录列表的方法。此方法只返回一个空列表,并且不会抛出任何异常。

我相信当我使用Visual Studio 2013将代码移动到新计算机时,我第一次遇到此问题。我使用的是.NET 4.5,并且在使用.NET 4.5.1时也有相同的行为。

问题:

  1. 为什么这段代码在我找到的大多数在线示例中使用了多年并使用了相同的技术?什么可能导致此代码突然停止工作?

  2. 有没有办法检测是否存在有效的目录?我想另一种方法是扫描父目录,虽然当例程应该验证根目录时逻辑需要不同。

6 个答案:

答案 0 :(得分:5)

我设法在我有权访问的其他网站上重现您的错误。经过一番游戏后,我的结论是: -

使用带有FtpWebRequest NOT 的FTP网址制作/时,例如:

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

你指定了WebRequestMethods.Ftp.ListDirectory作为方法,然后在场景后面做的是运行以下命令:

NLST "somefolder/invalidfolder"

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

  • 抛出ERROR 550:找不到文件或文件夹。 (示例: spftp v1.0
  • 返回空结果。 (示例: vsFTPd v2.0.5

在您的情况下,FTP服务器返回后一个响应,您的代码将失效。

解决方案?只需添加一些验证代码,以确保您尝试访问的ftp文件夹最后始终有/。类似于以下内容: -

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

答案 1 :(得分:1)

实际上,ftp类有一些奇怪的行为,但是你应该使用一个简单的控制台应用程序项目或者只是在原始项目中以这种方式实现你的目标。

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#版本(使用在线工具进行翻译请仔细检查代码)

   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;
    }
    }
}

我希望这能帮助你.Bye

答案 2 :(得分:0)

如上所述,也许FTP服务器配置发生了变化。您是否曾尝试以任何方式明确设置UsePassiveUseBinary属性的值?

答案 3 :(得分:0)

您可以尝试将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();

答案 4 :(得分:0)

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

_host.GetUrl(filename)应返回以“/”结尾的字符串。当存在与目标文件夹名称相同的文件时,不会抛出任何异常。

答案 5 :(得分:0)

如果您没有获得异常,作为可能的解决方法,您可以尝试请求文件夹的可预测属性,例如创建它的日期。

另一个建议是使用WireShark来查看幕后的FTP请求,这样可以让你看看它的.NET或服务器是否返回了无用的响应。