System.IProgress
- это интерфейс, используемый для того, чтобы мы могли писать собственные типы прогресса и обмениваться ими со встроенными. Он имеет один метод Report(T)
, где T
- анонимный тип.
Это означает, что вы можете написать свой собственный класс прогресса, но в .NET уже есть класс с таким интерфейсом, и, так как он соответствует требованиям, давайте его использовать. Он находится в том же пространстве имен, что и IProgress<T>
, и является Progress
, и поставляется с удобным встроенным событием ProgressChanged
, которое мы можем прослушивать. Так что в этом примере кода первого шага я только представляю прогресс. Обратите внимание, что я заменил ProgressBar
на переменную progress
в коде.
var progress = new Progress<double>();
progress.ProgressChanged += (s, progressValue) =>
{
//Update the UI (or whatever) with the progressValue
progressBar1.Value = Convert.ToInt32(progressValue);
};
INode myFile = myMegaClient.UploadFileAsync(filePathOnComputer, myFolderOnMega, progress, default());
Теперь я не буду преподавать вам урок по Task
, но знаю, что о Task
можно думать по-разному, как о потоках, но знайте, что Task
не всегда должен быть запущенным нить. В любом случае, дело в том, что мы используем CancellationToken
, чтобы сигнализировать об отмене Task
. Так как этот UploadFileAsync
является частью другого API, нам не нужно беспокоиться об обработке CancelationToken
, но мы можем предоставить один из них, чтобы попытаться отменить загрузку, если захотим. Просто обратите внимание, что в зависимости от API и отмены Task
потенциально может выдать ошибку (обычно OperationCanceledException
) или подобное. В любом случае, если вы предоставили токен, проверьте также его отмену, чтобы увидеть, как все это закончится.
В этом примере кода я покажу вам, как вы предоставляете CancellationToken
. Помните, что вы можете вызвать отмену на этом токене с другой кнопки, которая может сказать что-то вроде (Stop Upload).
Итак, во-первых, мы сделаем CancellationTokenSource
и сделаем его на уровне class
, чтобы мы могли использовать его в любом месте нашего класса.
private CancellationTokenSource uploadCancellationTokenSource = new CancellationTokenSource();
Затем, перед нашим UploadFileAsync
вызовом, мы должны убедиться, что он никогда не был отменен, и, если он есть, мы должны возобновить его.
if (uploadCancellationTokenSource.IsCancellationRequested)
{
uploadCancellationTokenSource.Dispose();
uploadCancellationTokenSource = new CancellationTokenSource();
}
INode myFile = myMegaClient.UploadFileAsync(filePathOnComputer, myFolderOnMega, progress, uploadCancellationTokenSource.Token);
И ... мы можем добавить событие нажатия кнопки или что-то, чтобы отменить токен, если захотим.
private void CancelUploadButtonClick(object sender, EventArgs e)
{
if (!uploadCancellationTokenSource.IsCancellationRequested)
uploadCancellationTokenSource.Cancel();
}
Надеюсь, вы немного больше поймете, что происходит и как это реализовать. Вот лишь пример ложного кода всего изменения на вашем примере:
private CancellationTokenSource uploadCancellationTokenSource = new CancellationTokenSource();
public async Task<uploadFileData> uploadToMegaAsync(string megaFolderName, string megaFolderID, string filePathOnComputer, string newFileNameOnMega)
{
//Implemnt Struct
uploadFileData myMegaFileData = new uploadFileData();
//Start Mega Cient
var myMegaClient = new MegaApiClient();
//Login To Mega
myMegaClient.Login(Userrrr, Passss);
//Get All (File & Folders) in Mega Account
IEnumerable<INode> nodes = myMegaClient.GetNodes();
//Creat List Of All Folders In Mega Account
List<INode> megaFolders = nodes.Where(n => n.Type == NodeType.Directory).ToList();
//Choose Exist Folder In Mega Account By Name & Id
INode myFolderOnMega = megaFolders.Where(folder => folder.Name == megaFolderName && folder.Id == megaFolderID).FirstOrDefault();
//Upload The File
//Normal Upload
//INode myFile = myMegaClient.UploadFile(filePathOnComputer, myFolderOnMega);
//NEWLY ADDED
var progress = new Progress<double>();
progress.ProgressChanged += (s, progressValue) =>
{
//Update the UI (or whatever) with the progressValue
progressBar1.Value = Convert.ToInt32(progressValue);
};
//NEWLY ADDED
if (uploadCancellationTokenSource.IsCancellationRequested)
{
uploadCancellationTokenSource.Dispose();
uploadCancellationTokenSource = new CancellationTokenSource();
}
// Upload With progress bar
INode myFile = await myMegaClient.UploadFileAsync(filePathOnComputer, myFolderOnMega, progress, uploadCancellationTokenSource.Token);
//Rename The File In Mega Server
if (string.IsNullOrEmpty(newFileNameOnMega))
{
}
else
{
myMegaClient.Rename(myFile, newFileNameOnMega);
}
//Get Download Link
Uri downloadLink = myMegaClient.GetDownloadLink(myFile);
myMegaFileData.megaFileId = myFile.Id;
Clipboard.SetText(myMegaFileData.megaFileId);
myMegaFileData.megaFileType = myFile.Type.ToString();
myMegaFileData.megaFileName = myFile.Name;
myMegaFileData.megaFileOwner = myFile.Owner;
myMegaFileData.megaFileParentId = myFile.ParentId;
myMegaFileData.megaFileCreationDate = myFile.CreationDate.ToString();
myMegaFileData.megaFileModificationDate = myFile.ModificationDate.ToString();
myMegaFileData.megaFileSize = myFile.Size.ToString();
myMegaFileData.megaFileDownloadLink = downloadLink.ToString();
myMegaClient.Logout();
return myMegaFileData;
}
private void CancelUploadButtonClick(object sender, EventArgs e)
{
if (!uploadCancellationTokenSource.IsCancellationRequested)
uploadCancellationTokenSource.Cancel();
}
Я собираюсь закончить еще одним советом. Поскольку у нас есть одноразовый тип класса (CancelletionTokenSource
), мы должны правильно реализовать IDisposable
в вашем классе. Это код, показывающий ТОЛЬКО реализацию IDisposable
и CancellationTokenSource
, и его следует использовать, однако он лучше всего подходит для вашего приложения.
Если вы используете свой собственный класс ... (технически мы должны пометить это как запечатанный или предоставить и перезаписать Dispose(bool)
, но просто для объяснения это так.
public class Example : IDisposable
{
private CancellationTokenSource uploadCancellationTokenSource = new CancellationTokenSource();
public void Dispose()
{
uploadCancellationTokenSource.Dispose();
GC.SuppressFinalize(this);
}
~Example() => Dispose();
}
Если вы находитесь в WinForm или другом унаследованном классе, в котором уже реализовано IDisposable
.
public class Example2 : Example
{
private CancellationTokenSource uploadCancellationTokenSource = new CancellationTokenSource();
public new void Dispose()
{
uploadCancellationTokenSource.Dispose();
base.Dispose();
}
}
Хорошо, наконец, я обновил метод uploadToMega
в приведенном выше коде для чтения в качестве задачи. Это означает, что вызывающий метод, который использует uploadToMega
, теперь должен будет также использовать async
в сигнатуре метода и await
в вызове.
Примечание: вы можете избежать этого, оставив вещи такими же, какими они были до добавления async await
и просто добавив .Result
в конец метода UploadFileAsync
, но понимая, что поток теперь удерживается здесь до тех пор, пока не завершится. Так что, если это поток пользовательского интерфейса, вы получите осложнения и, возможно, потеряете обновление на индикатор выполнения. Идея состоит в том, что вы хотите, чтобы этот вызов был асинхронным, но для понимания, вот строка ниже с добавленным .Result;
.
INode myFile = myMegaClient.UploadFileAsync(filePathOnComputer, myFolderOnMega, progress, uploadCancellationTokenSource.Token).Result;
Если вы хотите, также обновите ваш вопрос кодом, который на самом деле вызывает uploadToMega
, и я покажу вам, как мы можем обновить эту часть. В конце концов это изменение будет оценено.
Чтобы лучше понять, что здесь происходит, я советую вам взглянуть на тип Task
, понять, как он работает, а также посмотреть на ключевые слова async await
, которые позволяют работать с Task
в более удобной форме. .
Не стесняйтесь, присылайте мне весь проект на michael_puckett_ii@hotmail.com, и я обновлю его, уберу все ошибки компиляции и оставлю некоторые комментарии, чтобы помочь вам понять изменения, если вам нужно.