简体   繁体   English

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

[英]Wait for Async file upload to complete while reporting progress

I'm using WebClient to asynchronously upload a file. 我正在使用WebClient异步上传文件。 I want to wait for the upload to finish since I have dependent actions afterward. 我想等待上传完成,因为之后我需要执行相关操作。
As far as I understand, the best option would have all the code asynchronous, but this would mean converting a lot of synchronous code to asynchronous code. 据我了解,最好的选择是使所有代码异步,但这意味着将许多同步代码转换为异步代码。

Right now the file upload is running asynchronously and reporting the progress. 现在,文件上传正在异步运行并报告进度。
However, waiting on the upload to finish does not work. 但是,等待上载完成不起作用。

This is inspired by this article from Stephen Cleary (section "Vertical Partitions", Figure 6). 这是从Stephen Cleary的这篇文章 (“垂直分区”部分,图6)中获得的启发。 The biggest difference is that the file upload has return type void, therefore I'm wrapping it in a task. 最大的区别是文件上传的返回类型为void,因此我将其包装在任务中。

I'm using .NET Framework 4.0 with the Microsoft.Bcl.Async Nuget package. 我使用带有Microsoft.Bcl.Async Nuget包的.NET Framework 4.0。
Right now this is used in a console application, but it might be called from a Winforms application in the future as well. 现在,它已在控制台应用程序中使用,但将来也可能会从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}%");
    }
}

Current console output: 当前控制台输出:

Finished in 100 ms 在100毫秒内完成
Progress: 1% 进展:1%
Progress: 2% 进展:2%
Progress: 3% 进展:3%
.. ..
Progress: 100% 进度:100%

Desired output: 所需的输出:

Progress: 1% 进展:1%
Progress: 2% 进展:2%
Progress: 3% 进展:3%
.. ..
Progress: 100% 进度:100%
Finished in [actualTime] ms 在[actualTime]毫秒内完成

What am I doing wrong? 我究竟做错了什么?

The client.UploadFileAsync() call only starts the upload, but returns before it's finished. client.UploadFileAsync()调用仅开始上传,但在完成之前返回。 So the Task you wrapped it in is also completed almost immediatly. 因此,您将其包装的任务几乎立即完成。

You should register to the UploadFileCompletedEvent and use a TaskCompletionSource to indicate when the upload is finished: 您应该注册到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);
    }
}

You could even enhance this a little by evaluating the UploadFileCompletedEventArgs in the event handler and returning the actual results: 您甚至可以通过在事件处理程序中评估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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM