【问题标题】:Wait for Async file upload to complete while reporting progress在报告进度的同时等待异步文件上传完成
【发布时间】:2018-12-20 15:40:03
【问题描述】:

我正在使用WebClient 异步上传文件。我想等待上传完成,因为之后我有相关操作。
据我了解,最好的选择是让所有代码都异步,但这意味着将大量同步代码转换为异步代码。

目前文件上传正在异步运行并报告进度。
但是,等待上传完成是行不通的。

这是受到 Stephen Cleary 的 this article 的启发(“垂直分区”部分,图 6)。最大的区别是文件上传的返回类型为 void,因此我将它包装在一个任务中。

我正在使用带有 Microsoft.Bcl.Async Nuget 包的 .NET Framework 4.0。
目前它在控制台应用程序中使用,但将来也可能从 Winforms 应用程序中调用。

static void Main(string[] args)
{
    var uri = new Uri("https://somefileuploadurl.com");
    string file = @"C:\file.zip";

    var watch = Stopwatch.StartNew();
    var fileUploader = new FileUploader();
    fileUploader.UploadFile(uri, file);
    watch.Stop();

    Console.WriteLine($"Finished in {watch.ElapsedMilliseconds} ms");
    Console.ReadLine();
}

public class FileUploader
{
    public void UploadFile(Uri uri, string file)
    {
        UploadFileAsync(uri, file).GetAwaiter().GetResult();
    }

    public async Task UploadFileAsync(Uri uri, string file)
    {
        using (var client = new WebClient())
        {
            client.UploadProgressChanged += UploadProgressChanged;
            await Task.Factory.StartNew(() => client.UploadFileAsync(uri, "PUT", file))
                              .ConfigureAwait(false);
        }
    }

    private void UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
    {
        Console.WriteLine($"Progress: {e.ProgressPercentage}%");
    }
}

当前控制台输出:

在 100 毫秒内完成
进度:1%
进度:2%
进度:3%
..
进度:100%

期望的输出:

进度:1%
进度:2%
进度:3%
..
进度:100%
在 [actualTime] 毫秒内完成

我做错了什么?

【问题讨论】:

    标签: c# async-await .net-4.0 base-class-library


    【解决方案1】:

    client.UploadFileAsync() 调用仅启动上传,但在完成之前返回。所以你包裹它的任务也几乎立即完成。

    您应该注册到UploadFileCompletedEvent 并使用TaskCompletionSource 来指示上传何时完成:

    public async Task UploadFileAsync(Uri uri, string file)
    {
        using (var client = new WebClient())
        {
            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
            client.UploadProgressChanged += UploadProgressChanged;
    
            // this sets the task to completed when the upload finished
            client.UploadFileCompleted += (sender, e) => tcs.SetResult(0);
    
            client.UploadFileAsync(uri, "PUT", file);
            await tcs.Task.ConfigureAwait(false);
        }
    }
    

    您甚至可以通过在事件处理程序中评估 UploadFileCompletedEventArgs 并返回实际结果来稍微增强这一点:

    // return the byte[] result
    public async Task<byte[]> UploadFileAsync(Uri uri, string file)
    {
        using (var client = new WebClient())
        {
            // use correct result type for taskcompletionsource
            TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
            client.UploadProgressChanged += UploadProgressChanged;
    
            client.UploadFileCompleted += (sender, e) =>
            {
                if (e.Cancelled) // the upload has been cancelled
                    tcs.SetCancelled();
                else if (e.Error != null)
                    tcs.SetException(e.Error); // or faulted with an exception
                else
                    tcs.SetResult(e.Result); // or finished and returned a byte[]
            }
    
            client.UploadFileAsync(uri, "PUT", file);
            await tcs.Task.ConfigureAwait(false);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-09
      • 2020-10-04
      • 1970-01-01
      • 2019-05-31
      • 1970-01-01
      • 1970-01-01
      • 2018-07-17
      • 2011-05-18
      相关资源
      最近更新 更多