Почему нет уведомления об ошибке для UploadFileAsync с WebClient?
Цитирование Метод WebClient.UploadFileAsync (Uri, String, String) Примечания
Файл отправляется асинхронно с использованием ресурсов потоков, которые автоматически выделяются из пула потоков. Чтобы получить уведомление о завершении загрузки файла, добавьте обработчик события в событие UploadFileCompleted .
Акцент на шахте.
Вы не получаете ошибок, потому что он выполняется в другом потоке, чтобы не блокировать текущий поток. Чтобы увидеть ошибку, вы можете обратиться к ней в указанном обработчике событий через UploadFileCompletedEventArgs.Exception
.
Мне было любопытно, почему используется WebClient
, а не HttpClient
, который уже является в основном асинхронным, но тогда мое предположение было связано с прогрессом загрузки.
Я бы предложил обернуть вызов WebClient
обработчиками событий в Task
, используя TaskCompletionSource
, чтобы воспользоваться преимуществами TAP.
Ниже приведены примеры, приведенные здесь Как: обернуть шаблоны EAP в задачу
public static async Task UploadFileAsync(string serverPath, string pathToFile, string authToken, IProgress<int> progress = null) {
serverPath = @"C:\_Series\S1\The 100 S01E03.mp4";
using (var client = new WebClient()) {
// Wrap Event-Based Asynchronous Pattern (EAP) operations
// as one task by using a TaskCompletionSource<TResult>.
var task = client.createUploadFileTask(progress);
var uri = new Uri($"http://localhost:50424/api/File/Upload?serverPath={WebUtility.UrlEncode(serverPath)}");
client.UploadFileAsync(uri, "POST", pathToFile);
//wait here while the file uploads
await task;
}
}
Где createUploadFileTask
- это пользовательский метод расширения, используемый для переноса операций асинхронного шаблона на основе событий (EAP) WebClient
как одной задачи с использованием TaskCompletionSource<TResult>
.
private static Task createTask(this WebClient client, IProgress<int> progress = null) {
var tcs = new TaskCompletionSource<object>();
#region callbacks
// Specifiy the callback for UploadProgressChanged event
// so it can be tracked and relayed through `IProgress<T>`
// if one is provided
UploadProgressChangedEventHandler uploadProgressChanged = null;
if (progress != null) {
uploadProgressChanged = (sender, args) => progress.Report(args.ProgressPercentage);
client.UploadProgressChanged += uploadProgressChanged;
}
// Specify the callback for the UploadFileCompleted
// event that will be raised by this WebClient instance.
UploadFileCompletedEventHandler uploadCompletedCallback = null;
uploadCompletedCallback = (sender, args) => {
// unsubscribing from events after asynchronous
// events have completed
client.UploadFileCompleted -= uploadCompletedCallback;
if (progress != null)
client.UploadProgressChanged -= uploadProgressChanged;
if (args.Cancelled) {
tcs.TrySetCanceled();
return;
} else if (args.Error != null) {
// Pass through to the underlying Task
// any exceptions thrown by the WebClient
// during the asynchronous operation.
tcs.TrySetException(args.Error);
return;
} else
//since no result object is actually expected
//just set it to null to allow task to complete
tcs.TrySetResult(null);
};
client.UploadFileCompleted += uploadCompletedCallback;
#endregion
// Return the underlying Task. The client code
// waits on the task to complete, and handles exceptions
// in the try-catch block there.
return tcs.Task;
}
Пройдя еще один шаг и создав еще один метод расширения, чтобы обернуть загружаемый файл, чтобы он мог его ждать ...
public static Task PostFileAsync(this WebClient client, Uri address, string fileName, IProgress<int> progress = null) {
var task = client.createUploadFileTask(progress);
client.UploadFileAsync(address, "POST", fileName);//this method does not block the calling thread.
return task;
}
Разрешил рефакторинг вашего UploadFile
до
public static async Task UploadFileAsync(string serverPath, string pathToFile, string authToken, IProgress<int> progress = null) {
using (var client = new WebClient()) {
var uri = new Uri($"http://localhost:50424/api/File/Upload?serverPath={WebUtility.UrlEncode(serverPath)}");
await client.PostFileAsync(uri, pathToFile, progress);
}
}
Теперь это позволяет асинхронно вызывать загрузку и даже отслеживать прогресс с помощью собственного Отчета о ходе выполнения (необязательно)
Например, если на платформе XAML
public class UploadProgressViewModel : INotifyPropertyChanged, IProgress<int> {
public int Percentage {
get {
//...return value
}
set {
//...set value and notify change
}
}
public void Report(int value) {
Percentage = value;
}
}
Или с использованием «из коробки» Progress<T> Class
Так что теперь вы сможете загружать файл, не блокируя поток, и все же иметь возможность ожидать его, получать уведомления о ходе выполнения и обрабатывать исключения, если у вас есть попытка / отлов на месте.