Ваш код синхронный, интерфейс просто не будет обновляться пока он работает. Придется либо втыкать костыли в виде опасного Application.DoEvents() либо написать нормальный асинхронный метод, что я и сделал.
На самом деле, если поискать, примеров в гугле очень много, разных, в том числе и тот, который приведен в вопросе. Но все они сильно устаревшие, и многие не поддерживают докачку, либо портят файл при докачке.
К тому же HttpWebRequest устарел.
Не рекомендуется использовать HttpWebRequest для новой разработки. Вместо этого используйте класс HttpClient.
Вот пример для WinForms с использованием HttpClient.
- Грузит файл асинхронно и сохраняет на диск
- Докачивает, если часть файла уже загружена
- По нажатию кнопки Cancel можно остановить загрузку и продолжить потом повторным нажатием кнопки Download
button1 - кнопка Download, button2 - кнопка Cancel, progressBar1 - прогресс бар
- реализована базовая обработка ошибок
- реализован отчет о прогрессе с помощью интерфейса
IProgress и класса Progress
Полный код приложения.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button2.Enabled = false; // по умолчанию закачка не активна, кнопку `Cancel` выключаем
}
// HttpClient создается один раз на всё время работы приложения
private static readonly HttpClient _client = new HttpClient();
// Токен отмены служит для прерывания работы загрузчика в любой момент
private CancellationTokenSource _cts;
// метод универсален, проверен в .NET Core и .NET Framework
private async Task DownloadAndSaveFileAsync(string url, string path, IProgress<int> status, CancellationToken token)
{
const int bufferLength = 8192;
long currentPosition = File.Exists(path) ? new FileInfo(path).Length : 0;
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url))
{
request.Headers.Range = new RangeHeaderValue(currentPosition, null);
using (HttpResponseMessage response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
using (Stream responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.None))
{
long contentLength = currentPosition + response.Content.Headers.ContentLength ?? 0;
int progress = -1;
int oldProgress;
byte[] buffer = new byte[bufferLength];
int bytesReceived;
while ((bytesReceived = await responseStream.ReadAsync(buffer, 0, bufferLength, token).ConfigureAwait(false)) > 0)
{
await fs.WriteAsync(buffer, 0, bytesReceived, token).ConfigureAwait(false);
currentPosition += bytesReceived;
oldProgress = progress;
progress = (int)(currentPosition * 100 / contentLength);
// так как значение от 0 до 100, нет особого смысла повтороно обновлять интерфейс, если значение не изменилось.
if (oldProgress != progress)
{
status?.Report(progress);
}
}
}
}
}
}
// обратите внимание на async здесь
private async void button1_Click(object sender, EventArgs e)
{
if (_cts != null)
return;
button1.Enabled = false;
button2.Enabled = true;
using (_cts = new CancellationTokenSource())
{
try
{
// укажите здесь нужный URL и путь к файлу
// обратите внимение на new Progress<int>(v => progressBar1.Value = v) - оно и меняет значение прогресс бара во время загрузки
await DownloadAndSaveFileAsync("https://example.org/file.txt", "file.txt", new Progress<int>(v => progressBar1.Value = v), _cts.Token);
}
catch (OperationCanceledException) { }
catch (HttpRequestException ex)
{
if (ex.Message.Contains("416")) // 416 (Requested Range Not Satisfiable)
{
MessageBox.Show("Файл уже закачан");
}
else
{
MessageBox.Show(ex.ToString(), "HttpRequestException");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), ex.GetType().Name);
}
progressBar1.Value = 0;
}
_cts = null;
button1.Enabled = true;
button2.Enabled = false;
}
private void button2_Click(object sender, EventArgs e)
{
if (_cts != null && !_cts.IsCancellationRequested)
_cts.Cancel();
}
}
В данном примере есть один недостаток, он не будет работать если веб-сервер не поддерживает докачку файла. Но вы можете его доработать.
WebClient. У него есть событиеDownloadProgressChanged, которое срабатывает при изменении прогресса загрузки. Останется только подписатся на это событие и в подписчике обновлять вашProgressBar. – Boris Makhlin May 31 '20 at 08:46