简体   繁体   中英

Wait for Async file upload to complete while reporting progress

I'm using WebClient to asynchronously upload a file. 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). The biggest difference is that the file upload has return type void, therefore I'm wrapping it in a task.

I'm using .NET Framework 4.0 with the Microsoft.Bcl.Async Nuget package.
Right now this is used in a console application, but it might be called from a Winforms application in the future as well.

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
Progress: 1%
Progress: 2%
Progress: 3%
..
Progress: 100%

Desired output:

Progress: 1%
Progress: 2%
Progress: 3%
..
Progress: 100%
Finished in [actualTime] ms

What am I doing wrong?

The client.UploadFileAsync() call only starts the upload, but returns before it's finished. 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:

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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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