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

How can I write out correctly the download status in C# console app?

我有以下代码,除了写出下载状态外,它工作正常。

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();   

        }
    }
}

使用代码将插入许多新行并且不会更新和打印下载状态。

在我的例子中,如果您也处理 DownloadFileCompleted 事件,它就可以工作。

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

你还应该使用 client.Dispose() 或将你的代码写在 使用语句:

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

这将自动释放资源。

编辑:

正如 rene 正确注意到的那样,在这种情况下没有必要使用 Dispose但通常最好记住 using 语句通常与 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();   

        }
    }
}

为了什么 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();
}

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();
}