Как загрузить изображение из интернета, показывающее ход загрузки на c# с помощью winforms?

я загружаю изображение с URL-адреса асинхронно, используя WebRequest таким образом:

public void Download(string url)
{   
  byte[] buffer = new byte[0x1000];
  WebRequest request = HttpWebRequest.Create(url);
  request.Method = "GET";
  request.ContentType = "image/gif";

  request.BeginGetResponse(result =>
  {
    WebRequest webRequest = result.AsyncState as WebRequest;

    WebResponse response = webRequest.EndGetResponse(result);
    ReadState readState = new ReadState()
    {
      Response = response.GetResponseStream(),
      AccumulatedResponse = new MemoryStream(),
      Buffer = buffer,
    };

    readState.Response.BeginRead(buffer, 0,
      readState.Buffer.Length, ReadCallback, readState);
  }, request);
}

public void ReadCallback(IAsyncResult result)
{
  ReadState readState = result.AsyncState as ReadState;
  int bytesRead = readState.Response.EndRead(result);
  if(bytesRead > 0)
  {
    readState.AccumulatedResponse.BeginWrite(readState.Buffer, 0, bytesRead, writeResult =>
    {
      readState.AccumulatedResponse.EndWrite(writeResult);
      readState.Response.BeginRead(readState.Buffer, 0, readState.Buffer.Length, ReadCallback, readState);
    }, null);
  }
  else
  {
    readState.AccumulatedResponse.Flush();
    readState.Response.Close();
    pictureBox1.Image = Image.FromStream(readState.AccumulatedResponse);
  }
}

public class ReadState
{
  public Stream Response { get; set; }
  public Stream AccumulatedResponse { get; set; }
  public byte[] Buffer { get; set; }
}

и он работает нормально, но я хотел бы показать прогресс загрузки как браузеры, а не показывать только, когда он закончит.

Если я делаю

pictureBox1.Image = Image.FromStream(readState.AccumulatedResponse);

прежде чем он закончит, я получаю исключение, что изображение недействительно, хотя у него есть некоторые данные. Есть ли вообще, чтобы показать частичные данные?

7 ответов


JPEG имеет специальный режим кодирования под названием "Progressive JPEG", в котором данные сжимаются в несколько проходов постепенно более высокой детализации. Windows 7 имеет встроенная поддержка для этого.


Я хотел бы показать прогресс скачать как браузеры, и не показывать, только когда закончишь.

у вас есть два варианта:

  1. использовать WebClient.DownloadDataAsync. Это вызовет события прогресса через DownloadProgressChanged и предоставить окончательное уведомление, когда данные доступны через DownloadDataCompleted событие. В этот момент Вы можете назначить изображение, скажем, PictureBox.

  2. если вы загружаете изображение, чтобы в конечном итоге отобразить в PictureBox контроль тогда может быть даже проще просто пойти с PictureBox.LoadAsync. Это тоже обеспечит обновления прогресса через его LoadProgressChanged событие и, наконец,LoadCompleted по окончании.


У вас проблема с элементом управления PictureBox, а не с частичной загрузкой.

Хак должен использовать WebBrowserControl, так как IE может показывать частичные изображения.


когда вы начинаете запрос, проверьте метода webresponse.ContentLength свойство, которое должно дать вам общее количество ожидаемых байтов. Затем отслеживайте общее количество байтов, прочитанных в методе ReadCallback. Остальное должно быть очевидно. :)

[edit] Ой, я перечитал вопрос, и это было не совсем то, что вы хотели. Я все еще оставляю это, на случай, если кто-то может извлечь из этого выгоду.


Я не думаю, что вы справитесь с этим, используя встроенные методы .NET framework, так как они сначала прочитают полное изображение, а затем отобразят его. Вам нужно будет либо найти другую библиотеку, поддерживающую отображение частично загруженных изображений, либо написать парсер самостоятельно.


вы, вероятно, можете сделать это, однако, это потребует некоторых дополнительных знаний о структуре gif-файлов. Wotsit.org имеет несколько ссылок на документы структуры gif,чтобы вы начали.

общий подход будет заключаться в том, чтобы взять ваши общие накопленные данные, и с конца, поиск назад, пока вы не найдете конец блока Терминатора. Данные между этим последним допустимым блоком и концом потока только частично завершены и должны считаться нежелательными данными. Вы хотите удалить его и добавить в поток конечный блок файла. Затем вы сможете загрузить этот отредактированный поток в PictureBox.


есть событие, которое можно использовать в асинхронном запросе веб-загрузки, который периодически вызывается и позволяет напрямую обновлять элемент управления ProgressBar. Вот пример того, как я использовал это событие:

    delegate void SetProgressDelegate(int percentComplete);
    /// <summary>
    /// A thread-safe method for updating the file download progress bar.
    /// </summary>
    /// <param name="bytesReceived"></param>
    /// <param name="totalBytes"></param>
    void SetDownloadProgress(int percentComplete)
    {
        if (this.InvokeRequired)
        {
            SetProgressDelegate d = new SetProgressDelegate(SetDownloadProgress);
            this.Invoke(d, new object[] { percentComplete });
        }
        else
        {
            this.progressFileDownload.Value = percentComplete;
        }
    }
    /// <summary>
    /// Event handler to update the progress bar during file download.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void web_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        SetDownloadProgress(e.ProgressPercentage);
    }

и вот как я подключаю событие и начинаю загрузку:

            WebClient web = new WebClient();
            web.DownloadFileCompleted += new AsyncCompletedEventHandler(web_DownloadFileCompleted);
            web.DownloadProgressChanged += new DownloadProgressChangedEventHandler(web_DownloadProgressChanged);
            web.DownloadFileAsync(sourceUri, destinationFileName);

ваше решение считывает файл непосредственно в память, тогда как мое сохраняет его в файл на диске, поэтому это решение может работать или не работать для вас. Если отображается прогресс индикация важна для вас, этот метод работает, и вы всегда можете загрузить во временный файл, а затем загрузить этот файл в элемент управления image после его завершения.