【问题标题】:Download FTP file from a (specific) server works on .NET 4+, but doesn't work on .NET 2.0从(特定)服务器下载 FTP 文件适用于 .NET 4+,但不适用于 .NET 2.0
【发布时间】:2017-01-25 18:18:27
【问题描述】:

更新(这个问题的原因):

好的,我设法 -在@MitchelSellers 的评论的帮助下 - 找出问题的确切位置,并且我正在努力寻找解决方案。

  • 显然此服务器不允许使用命令CWD(更改工作目录)。
  • 现在使用.NET 2.0 下载文件 - 出于未知原因 - 它在登录后发送此命令,后跟/,以将工作目录更改为当前工作目录!!(如如下所示,并且我还确认将 FileZilla 与另一台服务器一起使用)。
  • 同样如下所示,.NET 的最新版本并非如此,这就是它适用于 .NET 4.x 的原因。

所以,我正在尝试找到一种方法让它在 .NET 2.0 上工作。


原问题:

这是我的代码:

Private Sub DownloadTestFile()
    Dim filePath As String = "ftp://ftp.kohlandfrisch.com/testfile"
    Dim request As FtpWebRequest

    Dim buffer(1023) As Byte
    Dim bytesIn As Integer

    request = DirectCast(WebRequest.Create(filePath), FtpWebRequest)
    request.Method = WebRequestMethods.Ftp.DownloadFile
    request.Credentials = New NetworkCredential("username", "password")
    request.UseBinary = False
    request.UsePassive = True
    request.Proxy = Nothing

    Using stream As IO.Stream = request.GetResponse.GetResponseStream
        Using output = IO.File.Create(localFilePath)
            bytesIn = 1
            Do Until bytesIn < 1
                bytesIn = stream.Read(buffer, 0, 1024)
                If bytesIn > 0 Then output.Write(buffer, 0, bytesIn)
            Loop
        End Using
    End Using
End Sub

.NET 44.x 上运行该代码时,它工作得非常好。但是在.NET 2.0 (我必须使用.NET 2.0) 上运行它时,调用request.GetResponse 时会引发异常。这是异常消息:

远程服务器返回错误:(501) 参数中的语法错误 或论据。

我发现.NET 2.0发送的请求一定有问题,所以我决定使用Wireshark捕获请求和响应,我的假设是正确的,但我仍然不明白是什么问题正是因为我在两个.NET 版本上使用相同的代码

Wireshark 结果

.NET 4.5.2

.NET 2.0

有什么想法吗?

旁注:虽然我的代码是用 VB 编写的,但欢迎使用 C# 代码回答。

【问题讨论】:

  • 尝试设置.UsePassive = False 看看是否适用于2.0...
  • @Zaggler,已经尝试过了。更改 .UsePassive.UseBinary 或删除 .Proxy = Nothing 并不能解决问题。
  • 尝试在文件 url 中使用 %2f 而不是 /。
  • @MitchelSellers,那会抛出一个UriFormatException(试过了)。我认为character (/) not allowed in object name 不是指文件路径,我认为它指的是请求CWD FTPCAS/,它只从.NET 2.0 发送!
  • 看起来像。我只是找不到任何具体的文档说明原因。给您带来问题的是 CWD 呼叫

标签: c# .net vb.net ftp ftpwebrequest


【解决方案1】:

根据评论更新:

.NET 2 不尊重该标志 :-( 它包含一个名为 BuildCommandsList() 的方法 - .NET 2 中的部分源代码如下所示:

if (m_PreviousServerPath != newServerPath) { 
    if (!m_IsRootPath
        && m_LoginState == FtpLoginState.LoggedIn
        && m_LoginDirectory != null)
    { 
        newServerPath = m_LoginDirectory+newServerPath;
    } 
    m_NewServerPath = newServerPath; 

    commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", newServerPath), PipelineEntryFlags.UserCommand)); 
}

所以基本上 CWD 在 .NET 2 中是硬编码的。 这给您留下了 2 个不受欢迎的“选项”:要么使 FTP 服务器符合 RFC(目前不符合!),要么使用其他库/方式来访问它。

一种非常不寻常的方法可能是从您的 .NET 应用程序调用/自动化 DOS 命令 ftp...

留作参考:

MS 在此处记录了 .NET 2 和 .NET 4 之间的这种行为变化:https://support.microsoft.com/en-au/help/2134299/system.net.ftpwebrequest-class-behaves-differently-in-.net-framework-4-vs-.net-framework-3.5

我怀疑在 .NET 2.0 中删除描述的标志值得一试——类似于以下内容:

private static void SetMethodRequiresCWD()
{
        Type requestType = typeof(FtpWebRequest);
        FieldInfo methodInfoField = requestType.GetField("m_MethodInfo", BindingFlags.NonPublic | BindingFlags.Instance);
        Type methodInfoType = methodInfoField.FieldType;


        FieldInfo knownMethodsField = methodInfoType.GetField("KnownMethodInfo", BindingFlags.Static | BindingFlags.NonPublic);
        Array knownMethodsArray = (Array)knownMethodsField.GetValue(null);

        FieldInfo flagsField = methodInfoType.GetField("Flags", BindingFlags.NonPublic | BindingFlags.Instance);

        int MustChangeWorkingDirectoryToPath = 0x100;
        foreach (object knownMethod in knownMethodsArray)
        {
            int flags = (int)flagsField.GetValue(knownMethod);
            flags &= (~MustChangeWorkingDirectoryToPath);
            flagsField.SetValue(knownMethod, flags);
        }
}

抱歉 - 我现在不能尝试这个,所以这更像是一个建议......

编辑: 或者,您可以使用一些行为类似于 .NET 4 的 ftp 库。

【讨论】:

  • 不幸的是,我不能在这个项目中使用 3rd 方库。 +1 为您的建议。这真是个好主意。可悲的是,它不起作用:/。我相信它不起作用的原因是:对于任何数字n,其中n &lt; 0x100n &amp;= ~0x100 = n,以及foreach 循环返回的所有数字都低于256 (0x100)。有什么想法吗?
  • @AhmedAbdelhameed :这样做的想法是“取消设置”标志,因此行为(当 n
  • 我知道这是关于取消设置标志,但由于每个标志的每个值都没有改变,所以没有任何东西被取消。我在这里错过了什么吗?编辑:也许我在之前的评论中不清楚。发生的事情是:如果 n
  • @AhmedAbdelhameed 这只是意味着该标志不是 .NET2 的一部分 - 请参阅上面的更新
  • 不客气 - 如果您真的不能使用除 .NET2 之外的任何东西,您将不得不自动化/使用 ftp DOS 命令(可能认为我不会推荐任何东西)。
【解决方案2】:

您是否尝试过将 filePath 设为绝对路径?即

Dim filePath As String = "ftp://ftp.kohlandfrisch.com/%2ftestfile"

%2f 代表 / 字符。

这可能是一种解决方法。

【讨论】:

  • 上面的cmets中MitchelSellers已经提出了这个建议,并不能解决问题。
  • 在这种情况下,您需要提供有关错误的更多信息。在代码中使用 try catch 来获取异常信息,例如堆栈跟踪。 550 错误可能意味着几件事:文件不存在、权限问题等。服务器是否运行 Apache? IIS?使用 %2f 无疑是解决此问题的第一步。
  • 它返回错误:550,因为该文件实际上在根目录中不可用。使用相对路径时,服务器将工作目录更改为 文件确实存在的位置。
猜你喜欢
  • 1970-01-01
  • 2013-01-21
  • 1970-01-01
  • 2018-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-12
相关资源
最近更新 更多