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

时间:2011-01-04 06:01:01

标签: c# asynchronous ftp webclient

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

感谢。

3 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:2)

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

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

  

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

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

FtpWebRequest / WebClient没有明确尝试找出正在下载的文件的大小。它所做的只是它试图在(xxx bytes). / 125150命令的响应中查找RETR字符串。没有FTP RFC要求服务器应包含此类信息。 ProFTPD(请参阅data_pasv_open in 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);
    }
}

enter image description here

核心下载代码基于:
Upload and download a binary file to/from FTP server in C#/.NET

答案 2 :(得分:-1)

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