Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to show download progress from FTP

Tags:

I am downloading a file from an FTP site (Async) and need to update a progress bar. I have read MS documentation that states that this can be done is the WebClient class's GetWebRequest() is ovverriden so the 'UsePassive' property is set to 'false'. I have done this but 'DownloadProgressChanged' event argument ProgressPercentage is always == '0'.

Can someone tell me how to get this argument to start returning values?

This is the download method:

            FTPClient request = new FTPClient();
            request.Credentials = new NetworkCredential(user, password);
            request.DownloadProgressChanged += UpdateProgress;
            request.DownloadFileAsync(ftpepath,destinationpath);

This is the FTPClient (where i am overriding GetWebRequest()):

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

And my Callback if it helps:

 void UpdateProgress(object sender, DownloadProgressChangedEventArgs e)
        {
            dwnProgress.Value = e.ProgressPercentage;
            dwnprcnt.Text = PercentProgress.ToString() + "%";
        }
like image 865
Nick Avatar asked Feb 24 '10 18:02

Nick


People also ask

What is FTP command?

The ftp command uses the File Transfer Protocol (FTP) to transfer files between the local host and a remote host or between two remote hosts. Remote execution of the ftp command is not recommended. The FTP protocol allows data transfer between hosts that use dissimilar file systems.


2 Answers

UsePassive is used to "determine" who acts as the server when the connection for the file transfer is made, so it should not have anything to do with the actual status of the transfer. May I ask where you have read this?

Could it be that

  • the change is very small for a large file, so you don't see the percentagechanged?
  • that the file is smaller than ftpwebrequests internal buffer so you get the whole file before any "update"?

Can you set a breakpoint in UpdateProgress and see anything in any of e's properties?

And as a side note, since you are downloading the file async, another thread is used for the actual download. In your event method you probably want to do something like this:

void UpdateProgress(object sender, DownloadProgressChangedEventArgs e) {
    setProgress(e.ProgressPercentage);
    setText(e.ProgressPercentage.ToString() + "%");
}

private void setProgress(int progress){
    if (dwnProgress.InvokeRequired) {
        dwnProgress.Invoke(new Action<int>(setProgress), progress);
        return;
    }
    dwnProgress.Value = progress;
}

private void setText(string text) {
   if (dwnprcnt.InvokeRequired) {
       dwnprcnt.Invoke(new Action<string>(setText), text);
       return;
   }
   dwnprcnt.Text = text;
}

Which will queue the setmethods to the UI thread instead.

like image 74
Patrick Avatar answered Oct 11 '22 17:10

Patrick


I wanted to leave a comment to the above post but I am too new :(

Reference to MSDN on overriding the web request method:

http://msdn.microsoft.com/en-US/library/system.net.webclient.downloadprogresschanged(v=vs.80).aspx

However in answer to the OPs question if your FTP server is not set to accept active connections then setting WebClient.UsePassive = false will make no difference.

EDIT: I enabled System.Net.Tracing on my project and tried both passive and active modes and not only do both work as expected... TotalBytes is still -1 so my thinking is the note on MSDN in mistaken or we are missing something

The DownloadFileProgressChangedEventArgs do contain the total bytes received and if you know the file size you can calculate yourself.

There is likely a better way... But I used a quick FtpWebRequest to get the file size and then pass this to the DownloadProgressCallback method to update the progress bar.

Also what the above poster failed to mention is that your update progress method must invoke the control because it is being called by the thread created by DownloadFileAsync and you can only change a control from the thread that created it.

You should use the dispatcher for the control e.g.

if (!myCheckBox.Dispatcher.CheckAccess())
{
    myCheckBox.Dispatcher.BeginInvoke(new Action(
    delegate()
    {
        myCheckBox.IsChecked = true;
    }
    ));
}
else
{
    myCheckBox.IsChecked = true;
}

See http://msdn.microsoft.com/en-us/library/ms591206.aspx

like image 20
Dave Williams Avatar answered Oct 11 '22 15:10

Dave Williams