Как загрузить изображение из Интернета, показывающее ход загрузки в c # с помощью winforms? - PullRequest
8 голосов
/ 27 апреля 2009

Я загружаю изображение из 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 ]

5 голосов
/ 27 апреля 2009

JPEG имеет специальный режим кодирования, называемый «Прогрессивный JPEG», в котором данные сжимаются в несколько проходов с постепенно увеличивающейся детализацией. Windows 7 имеет встроенную поддержку для этого.

4 голосов
/ 07 мая 2009

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

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

4 голосов
/ 06 мая 2009

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

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

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

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

3 голосов
/ 27 апреля 2009

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

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

2 голосов
/ 07 мая 2009

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

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

2 голосов
/ 27 апреля 2009

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

1 голос
/ 13 мая 2009

Есть событие, которое вы можете использовать в асинхронном веб-запросе на загрузку, которое периодически вызывается и позволяет напрямую обновлять элемент управления 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);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...