C# 从FTP下载文件的进度-TotalByTestOreReceive始终为-1?

C# 从FTP下载文件的进度-TotalByTestOreReceive始终为-1?,c#,asynchronous,ftp,webclient,C#,Asynchronous,Ftp,Webclient,我正在尝试从带有进度条的FTP服务器下载文件 文件正在下载,ProgressChanged事件正在调用,但args TotalBytesToReceive始终为-1的事件除外。TotalBytes会增加,但如果没有总数,我无法计算百分比 我想我可以通过其他ftp命令找到文件大小,但我想知道为什么这不起作用 我的代码: FTPClient request = new FTPClient(); request.Credentials = credentials; request.DownloadPr

我正在尝试从带有进度条的FTP服务器下载文件

文件正在下载,ProgressChanged事件正在调用,但args TotalBytesToReceive始终为-1的事件除外。TotalBytes会增加,但如果没有总数,我无法计算百分比

我想我可以通过其他ftp命令找到文件大小,但我想知道为什么这不起作用

我的代码:

FTPClient request = new FTPClient();
request.Credentials = credentials;
request.DownloadProgressChanged += new DownloadProgressChangedEventHandler(request_DownloadProgressChanged);
//request.DownloadDataCompleted += new DownloadDataCompletedEventHandler(request_DownloadDataCompleted);
request.DownloadDataAsync(new Uri(folder + file));
while (request.IsBusy) ;


谢谢。

FTP不会像HTTP那样为您提供内容大小,您自己做可能会更好

FtpWebRequest FTPWbReq = WebRequest.Create("somefile") as FtpWebRequest;
FTPWbReq .Method = WebRequestMethods.Ftp.GetFileSize;

FtpWebResponse FTPWebRes = FTPWbReq.GetResponse() as FtpWebResponse;
long length = FTPWebRes.ContentLength;
FTPWebRes.Close();

所以我也有同样的问题。我通过首先检索文件大小来绕过它

        // Get the object used to communicate with the server.
        FtpWebRequest request = (FtpWebRequest)WebRequest.Create("URL");
        request.Method = WebRequestMethods.Ftp.GetFileSize;
        request.Credentials = networkCredential;
        FtpWebResponse response = (FtpWebResponse)request.GetResponse();

        Stream responseStream = response.GetResponseStream();
        bytes_total = response.ContentLength; //this is an int member variable stored for later
        Console.WriteLine("Fetch Complete, ContentLength {0}", response.ContentLength);
        response.Close();

        webClient = new MyWebClient();
        webClient.Credentials = networkCredential; ;
        webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(FTPDownloadCompleted);
        webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(FTPDownloadProgressChanged);
        webClient.DownloadDataAsync(new Uri("URL"));
然后在回调函数中进行计算

private void FTPDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
     progressBar.Value = (int)(((float)e.BytesReceived / (float)bytes_total) * 100.0);
}

对于FTP协议,
WebClient
通常不知道总下载大小。因此,您通常通过FTP获得
-1

请注意,该行为实际上与.NET文档相矛盾,后者表示for(其中
TotalBytesToReceive
的值来自):

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

但是你会很容易地发现很多关于这方面的问题,有效地表明这种行为并不总是如文档所示。
FtpWebResponse.ContentLength
仅对
GetFileSize
方法具有有意义的值

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


如果您的服务器没有提供大小信息,您必须在下载之前自己查询大小。使用和的完整解决方案:

核心下载代码基于:

似乎您必须提供一个更好的
WebClient
实现来处理这个问题。查找要覆盖的有趣属性/方法。查看了
WebClient
,但如果没有大量的黑客攻击,它几乎不可能实现。
        // Get the object used to communicate with the server.
        FtpWebRequest request = (FtpWebRequest)WebRequest.Create("URL");
        request.Method = WebRequestMethods.Ftp.GetFileSize;
        request.Credentials = networkCredential;
        FtpWebResponse response = (FtpWebResponse)request.GetResponse();

        Stream responseStream = response.GetResponseStream();
        bytes_total = response.ContentLength; //this is an int member variable stored for later
        Console.WriteLine("Fetch Complete, ContentLength {0}", response.ContentLength);
        response.Close();

        webClient = new MyWebClient();
        webClient.Credentials = networkCredential; ;
        webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(FTPDownloadCompleted);
        webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(FTPDownloadProgressChanged);
        webClient.DownloadDataAsync(new Uri("URL"));
private void FTPDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
     progressBar.Value = (int)(((float)e.BytesReceived / (float)bytes_total) * 100.0);
}
private void button1_Click(object sender, EventArgs e)
{
    // Run Download on background thread
    Task.Run(() => Download());
}

private void Download()
{
    try
    {
        const string url = "ftp://ftp.example.com/remote/path/file.zip";
        NetworkCredential credentials = new NetworkCredential("username", "password");

        // Query size of the file to be downloaded
        WebRequest sizeRequest = WebRequest.Create(url);
        sizeRequest.Credentials = credentials;
        sizeRequest.Method = WebRequestMethods.Ftp.GetFileSize;
        int size = (int)sizeRequest.GetResponse().ContentLength;

        progressBar1.Invoke(
            (MethodInvoker)(() => progressBar1.Maximum = size));

        // Download the file
        WebRequest request = WebRequest.Create(url);
        request.Credentials = credentials;
        request.Method = WebRequestMethods.Ftp.DownloadFile;

        using (Stream ftpStream = request.GetResponse().GetResponseStream())
        using (Stream fileStream = File.Create(@"C:\local\path\file.zip"))
        {
            byte[] buffer = new byte[10240];
            int read;
            while ((read = ftpStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                fileStream.Write(buffer, 0, read);
                int position = (int)fileStream.Position;
                progressBar1.Invoke(
                    (MethodInvoker)(() => progressBar1.Value = position));
            }
        }
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message);
    }
}