在BackgroundWorker中取消FTP下载C#

时间:2015-02-17 00:26:15

标签: c# ftp backgroundworker

我有以下方法从FTP服务器下载文件:

public bool DownloadFTP(string LocalDirectory, string RemoteFile,
                        string Login, string Password)
    {

        try
        {
            FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(RemoteFile);
            ftp.Credentials = new NetworkCredential(Login, Password);
            ftp.KeepAlive = false;
            ftp.Method = WebRequestMethods.Ftp.DownloadFile;
            Form1 form = new Form1();
            ftp.UseBinary = true;
            long pesoarchivo = obtenertamano(RemoteFile, Login, Password);

            ftp.Proxy = null;
            ftp.EnableSsl = false;

            LocalDirectory = Path.Combine(LocalDirectory, Path.GetFileName(RemoteFile));
            using (FileStream fs = new FileStream(LocalDirectory, FileMode.Create,
                                        FileAccess.Write, FileShare.None))
            using (Stream strm = ftp.GetResponse().GetResponseStream())
            {
                int buffLength = 2048;
                byte[] buff = new byte[buffLength];
                // Leer del buffer 2kb cada vez
                int contentLen=strm.Read(buff, 0, buffLength);
                int bytes = 0;

                try
                {

                    while (contentLen > 0)
                    {

                        fs.Write(buff, 0, contentLen);
                        contentLen = strm.Read(buff, 0, buffLength);

                        bytes += contentLen;
                        int totalSize = (int)(pesoarchivo) / 1000;

                        if (bytes != 0)
                        {
                            // Console.WriteLine(((bytes / 1000) * 100 / totalSize).ToString()+"     "+ totalSize.ToString());
                            backgroundWorker1.ReportProgress((bytes / 1000) * 100 / totalSize, totalSize);
                            //     label1.Text = ((bytes / 1000) * 100 / totalSize).ToString();
                        }
                        else { }



                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString())                }

            }

            return true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString())      
            return false;
        }

然后我有一个使用按钮调用的BackgroundWorker。  在BackgroundWorker" DoWork"事件我调用DownloadFTP方法:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {

              if (descargaFTP(folder + "\\folder\\", ftpServer+ file, User, Password))
                        {
                           MessageBox.Show("Download Completed");
            }



  }

我想添加一个按钮来取消FTPDownload。我该怎么办?

1 个答案:

答案 0 :(得分:1)

要做到这一点,首先要删除BackgroundWorker并使用FtpWebRequest类的异步成员。据我所知,同步方法不可取消,但您可以使用FtpWebRequest.Abort()方法取消异步操作。

例如:

// This needs to be an instance field instead of local variable, so that
// the object's `Abort()` method can be called elsewhere
private FtpWebRequest ftp;

public async Task DownloadFTPAsync(CancellationToken cancelToken,
   string LocalDirectory, string RemoteFile, string Login, string Password)
{

    try
    {
        ftp = (FtpWebRequest)FtpWebRequest.Create(RemoteFile);
        ftp.Credentials = new NetworkCredential(Login, Password);
        ftp.KeepAlive = false;
        ftp.Method = WebRequestMethods.Ftp.DownloadFile;
        ftp.UseBinary = true;

        // [PD]: I don't know what this method does or even what the name
        // means, but on the assumption that because it needs the login
        // and password information, it may be doing some longer network
        // I/O or something, I'm wrapping it in a task to ensure the calling
        // thread remains responsive.
        long pesoarchivo = await Task.Run(() => obtenertamano(RemoteFile, Login, Password));

        // This is invariant, so might as well calculate it here, rather
        // than over and over again in the stream I/O loop below
        int totalSize = (int)(pesoarchivo) / 1000;

        ftp.Proxy = null;
        ftp.EnableSsl = false;

        LocalDirectory = Path.Combine(LocalDirectory, Path.GetFileName(RemoteFile));
        using (FileStream fs = new FileStream(LocalDirectory, FileMode.Create,
                                    FileAccess.Write, FileShare.None))
        using (Stream strm = (await ftp.GetResponseAsync()).GetResponseStream())
        {
            int buffLength = 2048;
            byte[] buff = new byte[buffLength];
            int contentLen;

            // Check for cancellation. This will throw an exception
            // if cancellation was requested; if you'd prefer, you can
            // check the cancelToken.IsCancellationRequested property
            // instead and just return, or whatever.
            cancelToken.ThrowIfCancellationRequested();

            while ((contentLen = await strm.ReadAsync(buff, 0, buff.Length)) > 0)
            {
                await fs.WriteAsync(buff, 0, contentLen);

                // Here, the calling thread (i.e. the UI thread) is executing
                // the code so just update whatever progress indication
                // you want directly.
                label1.Text = ((fs.Position / 1000) * 100 / totalSize).ToString();

                // Keep checking inside the loop
                cancelToken.ThrowIfCancellationRequested();
            }
        }
    }
    finally
    {
        ftp = null;
    }
}

注意:

  • 我清理了一下处理实际I / O的循环。你所拥有的代码并没有像以前那样有条理。
  • 实际上可以通过调用Stream.CopyToAsync()完全替换I / O循环。但是,当然在这种情况下,您将无法进行进度更新。我假设进度更新是一个重要的功能,所以我保留了上面的代码,即可以制作它们。
  • 因为使用async方法可以将异常处理移动到调用者中,所以我将其删除并在那里进行整合。当然,如果你愿意,你可以这样做。

使用上面实现的下载代码,您可以像这样调用它(例如):

CancellationTokenSource _cancelTokenSource;

async void downloadButton_Click(object sender, EventArgs e)
{
    // User clicked the button to start download

    _cancelTokenSource = new CancellationTokenSource();

    try
    {
        // For example, update the UI button state so user can't start a
        // new download operation until this one is done, but they can cancel
        // this one.
        downloadButton.Enabled = false;
        cancelButton.Enabled = true;

        // I assume you know where to get the other method arguments, i.e.
        // from "LocalDirectory" on; your incomplete code example didn't
        // provide that detail. :(
        await DownloadFTPAsync(_cancelTokenSource.Token,
            LocalDirectory, RemoteFile, Login, Password);
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show("Download was cancelled by user.");
    }
    catch (Exception ex)
    {
        // Some other error occurred
        MessageBox.Show(ex.ToString())      
    }
    finally
    {
        downloadButton.Enabled = true;
        cancelButton.Enabled = false;

        _cancelTokenSource = null;
    }

}

如果用户点击您的取消按钮,您可以像这样处理Click事件:

void cancelButton_Click(object sender, EventArgs e)
{
    ftp.Abort();
    _cancelTokenSource.Cancel();
}

调用FtpWebRequest.Abort()方法将中断任何实际的异步FTP I / O操作,当然调用CancellationTokenSource.Cancel()将导致CancellationToken表示请求取消。