C# Как отображать процент загрузки на progress bar
Есть такой код, который скачивает и докачивает файл. Как можно отображать процесс скачивание на прогресс бар
static void DownloadFile(string sSourceURL, string sDestinationPath)
{
long iFileSize = 0;
int iBufferSize = 1024;
iBufferSize *= 1000;
long iExistLen = 0;
System.IO.FileStream saveFileStream;
if (System.IO.File.Exists(sDestinationPath))
{
System.IO.FileInfo fINfo =
new System.IO.FileInfo(sDestinationPath);
iExistLen = fINfo.Length;
}
if (iExistLen > 0)
saveFileStream = new System.IO.FileStream(sDestinationPath,
System.IO.FileMode.Append, System.IO.FileAccess.Write,
System.IO.FileShare.ReadWrite);
else
saveFileStream = new System.IO.FileStream(sDestinationPath,
System.IO.FileMode.Create, System.IO.FileAccess.Write,
System.IO.FileShare.ReadWrite);
System.Net.HttpWebRequest hwRq;
System.Net.HttpWebResponse hwRes;
hwRq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(sSourceURL);
hwRq.AddRange((int)iExistLen);
System.IO.Stream smRespStream;
hwRes = (System.Net.HttpWebResponse)hwRq.GetResponse();
smRespStream = hwRes.GetResponseStream();
iFileSize = hwRes.ContentLength;
int iByteSize;
byte[] downBuffer = new byte[iBufferSize];
while ((iByteSize = smRespStream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
saveFileStream.Write(downBuffer, 0, iByteSize);
}
saveFileStream.Close();
}
Ответы (1 шт):
Ваш код синхронный, интерфейс просто не будет обновляться пока он работает. Придется либо втыкать костыли в виде опасного 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 { RequestUri = new Uri(url) })
{
request.Headers.Range = new RangeHeaderValue(currentPosition, null);
using (HttpResponseMessage response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token))
{
response.EnsureSuccessStatusCode();
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
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)
{
button1.Enabled = false;
button2.Enabled = true;
if (_cts == null)
{
_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.Message + "\r\n\r\n" + ex.StackTrace, "HttpRequestException");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\r\n\r\n" + ex.StackTrace, "Exception");
}
finally
{
_cts.Dispose();
_cts = null;
}
progressBar1.Value = 0;
}
button1.Enabled = true;
button2.Enabled = false;
}
private void button2_Click(object sender, EventArgs e)
{
if (_cts != null && !_cts.IsCancellationRequested) _cts.Cancel();
}
}
В данном примере есть один недостаток, он не будет работать если веб-сервер не поддерживает докачку файла. Но вы можете его доработать.