Skip to main content
 首页 » 编程设计

c#之使用 TAP 的 WebClient 进度报告

2024年02月05日28mengfanrong

我想知道是否有一种方法可以在不使用 EAP(基于事件的异步模式)的情况下报告 WebClient 进度。 旧方法(使用 EAP)是:

var client = new WebClient(); 
client.DownloadProgressChanged += (s,e) => { //progress reporting } 
client.DownloadFileCompleted += (s,e) => { Console.Write("download finished" } 
client.DownloadFileAsync(file); 

对于 async/await 这可以写成:

var client = new WebClient(); 
client.DownloadProgressChanged += (s,e) => { //progress reporting } 
await client.DownloadFileTaskAsync(file); 
Console.Write("downlaod finished"); 

但在第二个示例中,我同时使用了 EAP 和 TAP(基于任务的异步模式)。 混合两种异步模式不是一种不好的做法吗?

有没有一种方法可以在不使用 EAP 的情况下实现同样的效果? 我已经阅读了有关 IProgress 接口(interface)的信息,但我认为没有办法使用它来报告 WebClient 进度。

请您参考如下方法:

坏消息是答案是NO!

好消息是任何 EAP API 都可以转换为 TAP API。

试试这个:

public static class WebClientExtensios 
{ 
    public static async Task DownloadFileTaskAsync( 
        this WebClient webClient,  
        Uri address,  
        string fileName,  
        IProgress<Tuple<long, int, long>> progress) 
    { 
        // Create the task to be returned 
        var tcs = new TaskCompletionSource<object>(address); 
 
        // Setup the callback event handler handlers 
        AsyncCompletedEventHandler completedHandler = (cs, ce) => 
        { 
            if (ce.UserState == tcs) 
            { 
                if (ce.Error != null) tcs.TrySetException(ce.Error); 
                else if (ce.Cancelled) tcs.TrySetCanceled(); 
                else tcs.TrySetResult(null); 
            } 
        }; 
 
        DownloadProgressChangedEventHandler progressChangedHandler = (ps, pe) => 
        { 
            if (pe.UserState == tcs) 
            { 
                progress.Report( 
                    Tuple.Create( 
                        pe.BytesReceived,  
                        pe.ProgressPercentage,  
                        pe.TotalBytesToReceive)); 
            } 
        }; 
 
        try 
        { 
            webClient.DownloadFileCompleted += completedHandler; 
            webClient.DownloadProgressChanged += progressChangedHandler; 
 
            webClient.DownloadFileAsync(address, fileName, tcs); 
 
            await tcs.Task; 
        } 
        finally 
        { 
            webClient.DownloadFileCompleted -= completedHandler; 
            webClient.DownloadProgressChanged -= progressChangedHandler; 
        } 
    } 
} 

然后像这样使用它:

void Main() 
{ 
    var webClient = new WebClient(); 
 
    webClient.DownloadFileTaskAsync( 
        new Uri("http://feeds.paulomorgado.net/paulomorgado/blogs/en"), 
        @"c:\temp\feed.xml", 
        new Progress<Tuple<long, int, long>>(t => 
        { 
            Console.WriteLine($@" 
        Bytes received: {t.Item1,25:#,###} 
   Progress percentage: {t.Item2,25:#,###} 
Total bytes to receive: {t.Item3,25:#,###}"); 
        })).Wait(); 
}