Поддающийся отмене подход с использованием интерфейса IProgress шаблона async / await, использующий преимущества перекрывающегося ввода-вывода , если он доступен. Обратитесь к KB156932 , чтобы определить, подходит ли ваш сценарий. Токен отмены проверяется перед открытием потоков, но в противном случае он выгружается в асинхронные методы потоков во время передачи файла.
Я провел очень мало бенчмаркинга, но подозреваю, что это практично только при отправке больших файлов. Производительность использования перекрывающегося ввода-вывода может снизиться при уменьшении размера файлов и, особенно, при уменьшении размера буфера.
public async Task FtpAsync(string sourceFile, Uri destinationUri, string user, SecureString password, IProgress<decimal> progress, CancellationToken token)
{
const int bufferSize = 128 * 1024; // 128kb buffer
progress.Report(0m);
var request = (FtpWebRequest)WebRequest.Create(destinationUri);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(user, password);
token.ThrowIfCancellationRequested();
using (var fileStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true))
{
using (var ftpStream = await request.GetRequestStreamAsync())
{
var buffer = new byte[bufferSize];
int read;
while ((read = await fileStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{
await ftpStream.WriteAsync(buffer, 0, read, token);
var percent = 100m * ((decimal)fileStream.Position / fileStream.Length);
progress.Report(percent);
}
}
}
var response = (FtpWebResponse)await request.GetResponseAsync();
var success = (int)response.StatusCode >= 200 && (int)response.StatusCode < 300;
response.Close();
if (!success)
throw new Exception(response.StatusDescription);
}