如何在C#控制台应用程序中正确写出下载状态?

问题描述:

我有下面的代码,除了写出下载的状态外它正常工作。如何在C#控制台应用程序中正确写出下载状态?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net; 
using System.Text; 
using System.Threading; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 

     static void clear() 
     { 

      Thread.Sleep(1500); 
      Console.SetCursorPosition(0, 0); 

     } 


     static void Main(string[] args) 
     { 
      var client = new WebClient();   

      client.DownloadProgressChanged += (o, e) => 
      { 

       Console.Write(e.ProgressPercentage + "% "); 
       clear(); 


      };    

      client.DownloadFileAsync(new Uri("http://XXX"), "file"); 

      Console.ReadKey(); 

     } 
    } 
} 

随着代码的许多新的行会被插入和下载的状态不会被更新,并打印出来。

DownloadProgressChanged事件可以被多个线程同时调用,所以你需要照顾它,例如以下所示的使用lock

也不要在事件处理程序使用Thread.Sleep()!这从来都不是好主意。在这种特殊情况下,当线程正在休眠时,下载无法继续,导致下载速度明显变慢。如果您想限制屏幕更新的频率(以降低CPU负载并避免屏幕闪烁),只需跳过上一个事件之后过早发生的事件即可。

static void Main(string[] args) 
{ 
    var client = new WebClient(); 

    Object LockObject = new Object(); 
    DateTime LastProgressUpdateTime = DateTime.MinValue; 
    long LastProgressUpdatePosition = -1; 
    TimeSpan DesiredProgressUpdatePeriod = TimeSpan.FromMilliseconds(1500); 

    client.DownloadProgressChanged += (o, e) => 
    { 
     //Prevent multipe concurrent thread to update progress at once 
     lock (LockObject) 
     { 
      // This prevents updating progress to value that is lower than 
      // what we already printed. This could happen when threads 
      // enters lock out of order. 
      if (LastProgressUpdatePosition > e.BytesReceived) 
       return; 

      // This is not neccessary, but prevents you to miss 100% progress event() 
      var isCompleted = e.TotalBytesToReceive != 0 && e.BytesReceived == e.TotalBytesToReceive; 

      // Check if desired time elapsed since last update 
      bool UpdatePeriodElapsed = DateTime.Now >= LastProgressUpdateTime + DesiredProgressUpdatePeriod; 

      if(isCompleted || UpdatePeriodElapsed) 
      { 
       Console.SetCursorPosition(0, 0); 
       Console.Write(e.ProgressPercentage + "%"); 
       LastProgressUpdatePosition = e.BytesReceived; 
       LastProgressUpdateTime = DateTime.Now; 
      } 
     } 
    }; 

    client.DownloadFileAsync(new Uri("..."), "..."); 
    Console.ReadKey(); 
} 
+0

谢谢你,完美的解决方案。 – roll

对我来说,它的工作原理,如果你将处理DownloadFileCompleted事件了。

client.DownloadFileCompleted += (e, s) => 
{ 
    Console.WriteLine("Completed!"); 
}; 

而且你也应该使用client.Dispose()或使用语句写在代码:

using (WebClient client = new WebClient()) 
{ 
    // Code which uses WebClient 
} 

这将会被自动配置资源。

编辑:

由于刘若英正确地注意到,有在这种情况下没有必要使用处置但一般的好,记得使用语句通常与IDisposible或使用IO操作。

+0

那的确是很好的做法,但它原来的[WebClient的自我没有实现IDispose,也不会释放时调用Dispose任何资源(https://*.com/a/39609469/578411 )但这是一个实现细节。 – rene

+0

呵呵,感谢纠正,男人学习了他的洞穴生活:) – pablocity

您可以在每次更新之前调用Console.Clear()。这将删除您的控制台中的所有文本。这将更改您的代码如下:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net; 
using System.Text; 
using System.Threading; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 

     static void clear() 
     { 

      Thread.Sleep(1500); 
      Console.SetCursorPosition(0, 0); 

     } 


     static void Main(string[] args) 
     { 
      var client = new WebClient();   

      client.DownloadProgressChanged += (o, e) => 
      { 
       Console.Clear(); 
       Console.Write(e.ProgressPercentage + "% "); 
       clear(); 


      };    

      client.DownloadFileAsync(new Uri("http://XXX"), "file"); 

      Console.ReadKey(); 

     } 
    } 
} 
+0

我想保留控制台的内容。 不想删除,只有一行。 – roll

+0

如果你想保留内容比你需要像Tom Chantler描述的这样一个助手类。 [post](https://*.com/questions/34920124/read-values-already-printed-to-the-console) –

为了什么Thread.Sleep?去掉它。

如果您想保留控制台的内容,然后保存光标坐标和每次设置。

using (var client = new WebClient()) 
{ 
    int left = Console.CursorLeft; 
    int top = Console.CursorTop; 

    client.DownloadProgressChanged += (o, e) => 
    { 
     Console.SetCursorPosition(left, top); 
     Console.Write(e.ProgressPercentage + "% "); 
    }; 

    client.DownloadFileAsync(...); 
    Console.ReadKey(); 
}