使用进度从FTP下载文件 – TotalBytesToReceive始终为-1?

我正在尝试从带有进度条的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) ; 

….

 static void request_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { if (e.TotalBytesToReceive == -1) { l.reportProgress(-1, FormatBytes(e.BytesReceived) + " out of ?" ); } else { l.reportProgress(e.ProgressPercentage, "Downloaded " + FormatBytes(e.BytesReceived) + " out of " + FormatBytes(e.TotalBytesToReceive) + " (" + e.ProgressPercentage + "%)"); } } 

….

 class FTPClient : WebClient { protected override WebRequest GetWebRequest(System.Uri address) { FtpWebRequest req = (FtpWebRequest)base.GetWebRequest(address); req.UsePassive = false; return req; } } 

谢谢。

所以我遇到了同样的问题。 我首先通过检索文件大小来解决它。

  // 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文档相矛盾,后者表示对于FtpWebResponse.ContentLength (其中TotalBytesToReceive的值来自):

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

但是你很容易发现很多关于这个的问题,有效地表明这种行为并不总是记录在案。 FtpWebResponse.ContentLength GetFileSize方法有意义的值。

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


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

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

在此处输入图像描述

核心下载代码基于:
在C#/ .NET中上传和下载二进制文件到/从FTP服务器下载

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