博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

使用BackgroundWorker 实现文件下载、异步提示

Posted on 2011-04-07 23:28  moss_tan_jun  阅读(407)  评论(0编辑  收藏  举报

 

 最近在做一个IIS日志分析的系统,由于日志文件是在服务器上,但是分析需要放到客户端进行(为了提高性能和安全性),第一步就是需要将日志文件从服务器上下载到客户机上,可以通过控制台程序实现些功能,并且这个下载过程应该是自动化的不需要人工干预。

     准备做一个可视化的WinForm界面,这就需要反映文件下载进度,要达到这个实时报告进度的功能,就需要进行异步操作,可以通过线程或BackgroundWorker 类去实现, 由于BackgroundWorker 类是.net2.0新增的组件类,所以就先通过一个小实例来体验一下BackgroundWorker的使用方法,以后面的文章中将会给出使用线程的方法。
     我们先来了解BackgroundWorker 类的基本功能:
     在单独的线程上执行操作,此类是.net2.0新增的类,在.net1.1上不能使用(只能使用线程实现相同功能)。如果有一个需要很长时间才能完成的操作,而且不希望用户界面中出现延迟,则可以使用BackgroundWorker类来在另一个线程上运行该操作。
     BackgroundWorker 类允许您在单独的专用线程上运行操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面 (UI) 似乎处于停止响应状态。如果您需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用 BackgroundWorker 类方便地解决问题。
     若要在后台执行耗时的操作,请创建一个 BackgroundWorker ,侦听那些报告操作进度并在操作完成时发出信号的事件。可以通过编程方式创建 BackgroundWorker ,也可以将它从 “工具箱” 的 “组件” 选项卡中拖到窗体上。如果在 Windows 窗体设计器中创建 BackgroundWorker ,则它会出现在组件栏中,而且它的属性会显示在“属性”窗口中。
     若要设置后台操作,请为 DoWork 事件添加一个事件处理程序。在此事件处理程序中调用耗时的操作。若要启动该操作,请调用 RunWorkerAsync 。若要收到进度更新通知,请对 ProgressChanged 事件进行处理。若要在操作完成时收到通知,请对 RunWorkerCompleted 事件进行处理。
     说明:您必须非常小心,确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。BackgroundWorker 事件不跨 AppDomain 边界进行封送处理。请不要使用 BackgroundWorker 组件在多个 AppDomain 中执行多线程操作。 
     如果后台操作需要参数,请在调用 RunWorkerAsync 时给出参数。在 DoWork 事件处理程序内部,可以从 DoWorkEventArgs..::.Argument 属性中提取该参数。
     下面我们一起来看实现过程,若大家发现有不对的地方,请指导,可以到我的blog留言
      运行界面:
    
 
核心代码:
  1. private void btn_down_Click(object sender, EventArgs e)
  2.         {
  3.             //指示 是否报告进度更新
  4.             backgroundWorker1.WorkerReportsProgress = true;
  5.             //指示 是否支持异步取消
  6.             backgroundWorker1.WorkerSupportsCancellation = true;
  7.             //启动异步操作,并可以传递object参数,如"ready go",
  8.             //以便在BackgroundWorker的_DoWork事件中使用此参数e.Argument
  9.             backgroundWorker1.RunWorkerAsync("ready go");
  10.             this.btn_cancel.Enabled = true;
  11.             this.btn_down.Enabled = false;
  12.         }
  13.         private void btn_cancel_Click(object sender, EventArgs e)
  14.         {
  15.             //取消异步操作
  16.             this.backgroundWorker1.CancelAsync();
  17.             this.btn_cancel.Enabled = false;
  18.             this.btn_down.Enabled = true;
  19.         }
  20. private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
  21.         {
  22.             //获取BackgroundWorker对象.
  23.             BackgroundWorker worker = sender as BackgroundWorker;
  24.             
  25.             
  26.             //e.Argument获取启动异步操作(backgroundWorker1.RunWorkerAsync("ready go");)时传递的object。
  27.             object obj = e.Argument;//可以进行其它操作......
  28.             //将下载结果分配给DoWorkEventArgs对象的Result属性(e.Result),
  29.             //以提供给BackgroundWorker的RunWorkerCompleted事件作为句柄。
  30.             e.Result = DownLoadLog(worker, e);
  31.         }
  32.         private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
  33.         {
  34.             //获取异步进度的百分比
  35.             this.progressBar1.Value = e.ProgressPercentage;
  36.             this.lbl_msg.Text = string.Format("下载已完成{0}%,{1}M/{2}M", e.ProgressPercentage, (Math.Round(decimal.Parse(e.UserState.ToString()) / (decimal)(1024 * 1024),2)).ToString(), _fileSize.ToString());
  37.         }
  38.         private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  39.         {
  40.             // 首先,事件处理发生错误时,抛出异常
  41.             if (e.Error != null)
  42.             {
  43.                 MessageBox.Show(e.Error.Message);
  44.             }
  45.             else if (e.Cancelled)
  46.             {
  47.                 // Next, handle the case where the user canceled the operation.
  48.                 // Note that due to a race condition in 
  49.                 // the DoWork event handler, the Cancelled
  50.                 // flag may not have been set, even though
  51.                 // CancelAsync was called.
  52.                 //其次,事件处理操作被用户取消。
  53.                 //注意:由于事件运行条件由DoWork事件控制,尽管CancelAsync()方法被触发,但是未被设置为Cancelled标记。
  54.                 this.lbl_msg.Text = "下载被取消!";
  55.             }
  56.             else
  57.             {
  58.                 // Finally, handle the case where the operation 
  59.                 // succeeded.
  60.                 this.lbl_msg.Text = bool.Parse(e.Result.ToString()) ? "下载成功!" : "下载失败!";
  61.             }
  62.             // Enable the Start button.
  63.             btn_down.Enabled = true;
  64.             // Disable the Cancel button.
  65.             btn_cancel.Enabled = false;
  66.         }
  67.         
  68.         /// <summary>
  69.         /// 下载日志
  70.         /// </summary>
  71.         /// <param name="worker"></param>
  72.         /// <param name="e"></param>
  73.         private bool DownLoadLog(BackgroundWorker worker, DoWorkEventArgs e)
  74.         {
  75.             long maxFileSize = ftp.GetFileSize(_remoteFileName);
  76.             _fileSize = Decimal.Round(((decimal)maxFileSize / (decimal)1024) / (decimal)1024, 2);
  77.             //ftp.Get()方法中包含进度的报告、进程的取消、文件下载操作
  78.             return ftp.Get(_remoteFileName, _localFolder, _localFileName, maxFileSize, worker, e);
  79.         }

        只贴出触发报告进度的事件 

  1. while (true)
  2.             {
  3.                 iBytes = socketData.Receive(buffer, buffer.Length, 0);
  4.                 output.Write(buffer, 0, iBytes);
  5.                 nowGetBytes += (long)iBytes;
  6.                 //进度
  7.                 int percentComplete = (int)((float)nowGetBytes / (float)maxFileNum * 100);
  8.                 if (worker.CancellationPending)//用户取消下载
  9.                 {
  10.                     e.Cancel = true;
  11.                     usercancel = true;
  12.                     break;
  13.                 }
  14.                 else
  15.                 {
  16.                     worker.ReportProgress(percentComplete, nowGetBytes.ToString());
  17.                 }
  18.                 if (iBytes <= 0)
  19.                 {
  20.                     break;
  21.                 }
  22.             }