Вопрос или проблема
Я использовал IProgress
и Task
для асинхронной загрузки и отображения процесса загрузки, поддерживая асинхронное отмену.
Создайте внутреннюю функцию UploadFileViaSftp и вызывайте ее во внешней функции через await Task.Run(() => UploadFileViaSftp(…)). Внешняя функция уже имеет блок catch, но ошибка OperationCanceledException все равно возникает.
private async void btn_save_Click(object sender, EventArgs e)
{
try
{
cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
progressForm = new ProgressForm(() =>
{
cancellationTokenSource.Cancel();
});
progressForm.Show();
var progressIndicator = new Progress<int>(percent =>
{
progressForm.UpdateProgress(percent);
});
await Task.Run(() => UploadFileViaSftp(client, token, progressIndicator, fileStream, pictureStream, remoteDirectory + "/" + fileName));
MessageBox.Show("успех");
}
catch (OperationCanceledException)
{
MessageBox.Show("отменено");
}
catch (Exception ex)
{
MessageBox.Show("ошибка" + ex.Message);
}
finally
{
}
}
private void UploadFileViaSftp(SftpClient sftpClient, CancellationToken cancellationToken, IProgress<int> progress, FileStream fileStream, FileStream picStream, string remoteFilePath)
{
bool isFileStartUploaded = false;
bool isPicStartUploaded = false;
try
{
if (!sftpClient.IsConnected)
{
sftpClient.Connect();
}
if (fileStream.CanRead && picStream.CanRead)
{
long totalBytes = fileStream.Length + picStream.Length;
long uploadedBytes = 0;
byte[] buffer = new byte[8192];
int bytesRead;
using (var uploadStream = sftpClient.OpenWrite(remoteFilePath))
{
isFileStartUploaded = true;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
cancellationToken.ThrowIfCancellationRequested();
uploadStream.Write(buffer, 0, bytesRead);
uploadedBytes += bytesRead;
int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);
progress.Report(percentComplete);
}
}
using (var uploadPicStream = sftpClient.OpenWrite(Path.ChangeExtension(remoteFilePath, ".png")))
{
isPicStartUploaded = true;
while ((bytesRead = picStream.Read(buffer, 0, buffer.Length)) > 0)
{
cancellationToken.ThrowIfCancellationRequested();
uploadPicStream.Write(buffer, 0, bytesRead);
uploadedBytes += bytesRead;
int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);
progress.Report(percentComplete);
}
}
isPicStartUploaded = true;
}
else
{
MessageBox.Show("доступ запрещен");
}
}
catch (OperationCanceledException)
{
if (!sftpClient.IsConnected)
{
sftpClient.Connect();
}
if (isFileStartUploaded && sftpClient.Exists(remoteFilePath))
{
sftpClient.DeleteFile(remoteFilePath);
}
string remotePicPath = Path.ChangeExtension(remoteFilePath, ".png");
if (isPicStartUploaded && sftpClient.Exists(remotePicPath))
{
sftpClient.DeleteFile(remotePicPath);
}
string remoteDirectory = Path.GetDirectoryName(remoteFilePath);
if (sftpClient.Exists(remoteDirectory) && sftpClient.ListDirectory(remoteDirectory).Count() == 0)
{
sftpClient.DeleteDirectory(remoteDirectory);
}
throw;
}
catch (Exception)
{
throw;
}
}
Что я могу сделать, когда внутренняя функция UploadFileViaSftp
перехватывает OperationCancelException и бросает ее в внешнюю функцию снова для дальнейшей обработки
Ответ или решение
Когда вы работаете с асинхронными задачами и методами, такими как UploadFileViaSftp
, важно правильно обрабатывать исключения, чтобы они могли быть пойманы в верхнем уровне вызова. В вашем случае исключение OperationCanceledException
не обрабатывается корректно из-за того, что оно перехватывается внутри функции UploadFileViaSftp
и выбрасывается снова, но при этом может не обрабатываться должным образом, если не учесть контекст вызова. Для решения этой проблемы необходимо внести изменения в код, чтобы исключение обрабатывалось правильно.
Вот несколько шагов, которые помогут решить вашу проблему:
-
Удалите
catch (OperationCanceledException)
вUploadFileViaSftp
, если вы планируете обработать это исключение в вызывающем методе. В этом случае просто выбросьте исключение дальше без обработки его в текущем методе. -
Используйте правильный подход для завершения операций, когда производится отмена. Если отмена вызвана, с помощью токена отмены вы можете просто выбросить это исключение.
-
Убедитесь, что вы правильно выбрасываете исключение в
UploadFileViaSftp
, если происходит отмена, и не обрабатываетеOperationCanceledException
, так как оно должно быть обработано в основном методе.
Вот обновлённая версия вашего кода:
private async void btn_save_Click(object sender, EventArgs e)
{
try
{
cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
progressForm = new ProgressForm(() =>
{
cancellationTokenSource.Cancel();
});
progressForm.Show();
var progressIndicator = new Progress<int>(percent =>
{
progressForm.UpdateProgress(percent);
});
// Вызов функции UploadFileViaSftp через Task.Run
await Task.Run(() => UploadFileViaSftp(client, token, progressIndicator, fileStream, pictureStream, remoteDirectory + "/" + fileName));
MessageBox.Show("Удаление завершено успешно");
}
catch (OperationCanceledException)
{
MessageBox.Show("Операция отменена");
}
catch (Exception ex)
{
MessageBox.Show("Ошибка: " + ex.Message);
}
finally
{
// Освобождение ресурсов, если требуется
}
}
private void UploadFileViaSftp(SftpClient sftpClient, CancellationToken cancellationToken, IProgress<int> progress, FileStream fileStream, FileStream picStream, string remoteFilePath)
{
bool isFileStartUploaded = false;
bool isPicStartUploaded = false;
if (!sftpClient.IsConnected)
{
sftpClient.Connect();
}
if (fileStream.CanRead && picStream.CanRead)
{
long totalBytes = fileStream.Length + picStream.Length;
long uploadedBytes = 0;
byte[] buffer = new byte[8192];
int bytesRead;
using (var uploadStream = sftpClient.OpenWrite(remoteFilePath))
{
isFileStartUploaded = true;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
cancellationToken.ThrowIfCancellationRequested();
uploadStream.Write(buffer, 0, bytesRead);
uploadedBytes += bytesRead;
int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);
progress.Report(percentComplete);
}
}
using (var uploadPicStream = sftpClient.OpenWrite(Path.ChangeExtension(remoteFilePath, ".png")))
{
isPicStartUploaded = true;
while ((bytesRead = picStream.Read(buffer, 0, buffer.Length)) > 0)
{
cancellationToken.ThrowIfCancellationRequested();
uploadPicStream.Write(buffer, 0, bytesRead);
uploadedBytes += bytesRead;
int percentComplete = (int)((double)uploadedBytes / totalBytes * 100);
progress.Report(percentComplete);
}
}
}
else
{
MessageBox.Show("Доступ к файлам запрещен");
}
}
В этом коде:
OperationCanceledException
не обрабатывается вUploadFileViaSftp
, что позволяет ему быть пойманным в методеbtn_save_Click
и соответствующим образом отреагировать на отмену операции.- Убедитесь, что блоки кода, отвечающие за удаление файлов и очистку, находятся в блоке
finally
в методеbtn_save_Click
или в самом методеUploadFileViaSftp
, если это необходимо.
Таким образом, вы обеспечите слежение за состоянием при отмене и корректное реагирование наCancellation.