等待异步文件上传完成,同时报告进度

时间:2018-07-12 11:57:47

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

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

现在文件上传正在异步运行并报告进度。
 但是,等待上传完成不起作用。

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

我正在将.NET Framework 4.0与Microsoft.Bcl.Async Nuget包一起使用。
现在,它已在控制台应用程序中使用,但将来也可能会从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]毫秒内完成

我在做什么错了?

1 个答案:

答案 0 :(得分:0)

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