简体   繁体   English

如何使用winforms从网上下载显示下载进度的图像?

[英]How to download an image from web showing the progress of the download in c# using winforms?

I download an image from an URL asynchronously using WebRequest this way: 我使用WebRequest以这种方式异步下载URL中的图像:

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; }
}

and it works ok, but I would like to show the progress of the download as the browsers do, and not to show only when it finishes. 并且它工作正常,但我想像浏览器那样显示下载的进度,而不是仅在完成时显示。

If I do 如果我做

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

before it finishes I get an exception that the picture is not valid, even though it has some data. 在它完成之前,我得到一个例外,即图片无效,即使它有一些数据。 Is there anyway to show partial data? 反正有没有显示部分数据?

JPEG has a special encoding mode called "Progressive JPEG" in which data is compressed in multiple passes of progressively higher detail. JPEG具有一种称为“渐进式JPEG”的特殊编码模式,其中数据在逐步更高细节的多次传递中被压缩。 Windows 7 has built-in support for this. Windows 7 内置了对此的支持

I would like to show the progress of the download as the browsers do, and not to show only when it finishes. 我希望像浏览器一样显示下载的进度,而不是仅在完成时显示。

You have two options: 您有两种选择:

  1. Use WebClient.DownloadDataAsync . 使用WebClient.DownloadDataAsync This will raise progress events via DownloadProgressChanged and provide a final notification of when the data is available via the DownloadDataCompleted event. 这将通过提高进度事件DownloadProgressChanged并提供当数据通过可用的最终通知DownloadDataCompleted事件。 At that point, you could assign the image to, say, the PictureBox . 此时,您可以将图像分配给PictureBox

  2. If you are downloading an image to eventually display in a PictureBox control then it may even be easier to just go with PictureBox.LoadAsync . 如果您正在下载图像的最终显示PictureBox控件,然后它甚至可能是更容易只是去PictureBox.LoadAsync This too will provide progress updates via its LoadProgressChanged event and finally LoadCompleted on completion. 这也将通过其LoadProgressChanged事件提供进度更新,最后在完成时提供LoadCompleted

You've problem with the PictureBox control, not with the partial download. 你有PictureBox控件的问题,而不是局部下载。

An hack is to use the WebBrowserControl, as IE is able to show partial images. 黑客就是使用WebBrowserControl,因为IE能够显示部分图像。

When you begin the request, check the WebResponse.ContentLength property, that should give you the total number of bytes to expect. 当您开始请求时,请检查WebResponse.ContentLength属性,该属性应该为您提供预期的总字节数。 Then keep track of the total number of bytes read in your ReadCallback method. 然后跟踪ReadCallback方法中读取的总字节数。 The rest should be obvious. 其余应该是显而易见的。 :) :)

[edit] Oops, I re-read the question, and that wasn't quite what you wanted. [编辑]哎呀,我重新阅读了这个问题,这不是你想要的。 I'm still leaving this up though, in case somebody might benefit from it. 我仍然会离开这个,以防有人可能从中受益。

I don't think you will manage that using the built-in methods of the .NET framework, since they will first read the full image and then display it. 我不认为您将使用.NET框架的内置方法来管理它,因为它们将首先读取完整的图像然后显示它。 You will need to either find some other library that supports displaying partially downloaded images or write a parser yourself. 您需要找到一些支持显示部分下载图像的其他库或自己编写解析器。

You can probably do this, however, it will require some additional knowledge of the structure of gif files. 你可以这样做,但是,它需要一些关于gif文件结构的额外知识。 Wotsit.org has several links to gif structure documents, to get you started. Wotsit.org有几个gif结构文档的链接,可以帮助您入门。

The general approach would be to take your total accumulated data, and from the end, search back until you find the end of a block terminator. 一般方法是获取总累积数据,并从最后搜索,直到找到块终止符的结尾。 The data between this last valid block and the end of the stream is only partially complete, and must be considered junk data. 最后一个有效块和流结束之间的数据只是部分完成,必须被视为垃圾数据。 You want to remove it, and add a file trailing block to the stream. 您想要将其删除,并将文件尾随块添加到流。 Then, you should be able to load this edited stream into a PictureBox. 然后,您应该能够将此编辑的流加载到PictureBox中。

There's an event you can use on an asynchronous web download request that gets called periodically and allows you to directly update a ProgressBar control. 您可以在异步Web下载请求上使用该事件,该请求会定期调用,并允许您直接更新ProgressBar控件。 Here's an example of how I've used this event: 这是我如何使用此事件的示例:

    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);
    }

And here's how I wire up the event and start the download: 这是我如何连接事件并开始下载:

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

Your solution reads the file directly into memory, whereas mine saves it to a file on disk, so this solution may or may not work for you. 您的解决方案将文件直接读入内存,而我的文件将其保存到磁盘上的文件中,因此该解决方案可能适用于您,也可能不适合您。 If displaying a progress indication is important to you, this method does work and you can always download to a temporary file, then load that file into the image control once it's complete. 如果显示进度指示对您很重要,则此方法可以正常工作,您始终可以下载到临时文件,然后在文件控件完成后将其加载到图像控件中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM