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.