【问题标题】:e.TotalBytesToReceive returns -1 [duplicate]e.TotalBytesToReceive 返回 -1 [重复]
【发布时间】:2018-04-25 07:35:11
【问题描述】:

我有一个包含 DirectAdmin 用户备份列表的 ListBox。列表使用WebRequestMethods.Ftp.ListDirectory 填充,如下所示:

我可以使用右下角的按钮下载档案。当我单击该按钮时,会出现另一个表单并下载存档。

我的下载代码是这样的:

public static void DownloadFile(string server, string username, ...)
{
    Uri URI = new Uri($"ftp://{server}/{targetFilePath}");
    using (WebClient client = new WebClient())
    {
        client.Credentials = new NetworkCredential(username, password);
        if (progress != null)
        {
            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(progress);
        }
        if (complete != null)
        {
            client.DownloadFileCompleted += new AsyncCompletedEventHandler(complete);
        }
        before?.Invoke();
        client.DownloadFileAsync(URI, localFilePath);
    }
}

这就是我为DownloadProgressChanged 事件传递给DownloadFile() 方法的内容:

delegate (object s2, DownloadProgressChangedEventArgs e2)
{
    TransferLabel.Invoke((MethodInvoker)delegate
    {
       TransferLabel.Text = $"{(e2.BytesReceived / 1024).ToString()} KB / {(e2.TotalBytesToReceive / 1024).ToString()} KB";
    });
    TransferProgressBar.Invoke((MethodInvoker)delegate
    {
       TransferProgressBar.Value = (int)(e2.BytesReceived / (float)e2.TotalBytesToReceive * 100);
    });
}

我正在使用同样的方法上传文件,它工作正常,但下载e2.TotalBytesToReceive 在整个过程中返回-1

只有当它完成时,我才能得到正确的值:

这是为什么呢?


我找到了解决问题的方法。我会将 ListBox 更改为 ListView,并使用 ListDirectoryDetails 存储档案的文件大小。这样我就可以将e.BytesReceived 与存储的总字节数进行比较,而不是e.TotalBytesToReceive。这将解决我的问题,但我仍然对这个问题感到好奇。为什么我会收到-1?我做错了什么,还是与服务器相关的问题?还有什么我可以做的来解决它(得到正确的值)?

【问题讨论】:

标签: c# download ftp webclient


【解决方案1】:

使用 FTP 协议,WebClient 通常不知道总下载大小。所以你通常会通过 FTP 获得-1

另见Download file from FTP with Progress - TotalBytesToReceive is always -1?

请注意,该行为实际上与 .NET 文档相矛盾,该文档说的是 FtpWebResponse.ContentLengthTotalBytesToReceive 的值来自哪里):

对于使用DownloadFile 方法的请求,如果下载的文件包含数据,则该属性大于零,如果为空,则该属性为零。

但是您会很容易地找到很多关于此的问题(例如我上面链接的那个),有效地表明该行为并不总是像记录的那样。 FtpWebResponse.ContentLength 仅对 GetFileSize 方法具有有意义的值。

FtpWebRequest/WebClient 没有明确尝试找出它正在下载的文件的大小。它所做的只是尝试在125/150RETR 命令的响应中查找(xxx bytes). 字符串。没有 FTP RFC 要求服务器应包含此类信息。 ProFTPD(参见data_pasv_open in src/data.c)和vsftpd(参见postlogin.c 中的handle_retr)似乎包含此信息。其他常见的 FTP 服务器(IIS、FileZilla)不这样做。

【讨论】:

  • “所以你总是用 FTP 得到 -1”——这不是真的。我刚刚检查了一些公共 ftp 服务器,它在使用 DownloadFile 时返回 ContentLength 就好了。
  • 我已经删除了它,但它是完全正常的。基本上只是new WebClient().DownloadFileAsync(new Uri("ftp://speedtest.tele2.net/500MB.zip"), output)(与DownloadProgressChanged 一样在OP 问题中)。
  • @Evk 好的,我已经相应地更新了我的答案。
【解决方案2】:

当然,对于 HTTP 下载,服务器可能在执行文件下载时不提供大小信息,并且在服务器发出完成信号之前,您将无法获得任何有意义的信息。

不确定 FTP(我注意到在 FTP 命令集中定义了一个单独的 SIZE 命令,因此在检索期间包含此类信息可能被认为是多余的)。

我有点惊讶,TotalBytesToRetrieve 的文档没有更明确地说明信息不可用的可能性以及在这种情况下将返回什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-20
    • 2011-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-13
    • 2020-01-25
    相关资源
    最近更新 更多