如何在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();
}
对我来说,它的工作原理,如果你将处理DownloadFileCompleted事件了。
client.DownloadFileCompleted += (e, s) =>
{
Console.WriteLine("Completed!");
};
而且你也应该使用client.Dispose()或使用语句写在代码:
using (WebClient client = new WebClient())
{
// Code which uses WebClient
}
这将会被自动配置资源。
编辑:
由于刘若英正确地注意到,有在这种情况下没有必要使用处置但一般的好,记得使用语句通常与IDisposible或使用IO操作。
您可以在每次更新之前调用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();
}
}
}
我想保留控制台的内容。 不想删除,只有一行。 – roll
如果你想保留内容比你需要像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();
}
谢谢你,完美的解决方案。 – roll