我正在使用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]毫秒内完成
我在做什么错了?
答案 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);
}
}