【问题标题】:How to download file into string with progress callback?如何使用进度回调将文件下载到字符串中?
【发布时间】:2010-04-26 11:45:11
【问题描述】:

我想使用WebClient(或者还有其他更好的选择?)但是有一个问题。我知道打开流需要一些时间,这是无法避免的。然而,与完全立即阅读相比,阅读它所花费的时间要多得多。

有没有最好的方法来做到这一点?我的意思是两种方式,字符串和归档。 Progress 是我自己的代表,效果很好。


第五次更新:

最后,我成功了。与此同时,我查看了一些解决方案,这些解决方案让我意识到问题出在其他地方。

我已经测试了自定义 WebResponseWebRequest 对象、库 libCURL.NET 甚至 Sockets

时间上的差异是 gzip 压缩。压缩后的流长度只是正常流长度的一半,因此使用浏览器下载时间不到 3 秒。

如果有人想知道我是如何解决这个问题的,我会放一些代码:(不需要一些标题)

public static string DownloadString(string URL)
    {
        WebClient client = new WebClient();
        client.Headers["User-Agent"] = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1045 Safari/532.5";
        client.Headers["Accept"] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
        client.Headers["Accept-Encoding"] = "gzip,deflate,sdch";
        client.Headers["Accept-Charset"] = "ISO-8859-2,utf-8;q=0.7,*;q=0.3";

        Stream inputStream = client.OpenRead(new Uri(URL));
        MemoryStream memoryStream = new MemoryStream();
        const int size = 32 * 4096;
        byte[] buffer = new byte[size];

        if (client.ResponseHeaders["Content-Encoding"] == "gzip")
        {
            inputStream = new GZipStream(inputStream, CompressionMode.Decompress);
        }

        int count = 0;
        do
        {
            count = inputStream.Read(buffer, 0, size);
            if (count > 0)
            {
                memoryStream.Write(buffer, 0, count);
            }
        }
        while (count > 0); 

        string result = Encoding.Default.GetString(memoryStream.ToArray());
        memoryStream.Close();
        inputStream.Close();
        return result;
    }

我认为异步函数将几乎相同。但我将简单地使用另一个线程来触发此功能。我不需要精确的进度指示。

【问题讨论】:

  • 究竟有什么不好?你写入的字符串在哪里?
  • 它不好,因为结果是文件末尾只有 2048 (iSize) 个字符。而且下载速度非常慢。
  • 您似乎在两个地方读到相同的StreamreaderstreamRemote...我很困惑...还有;注意:在处理字节时,您并不总是读取整数个字符的块 (iByteSize)。 UI 更新如何在回调中发生也不是很清楚;如果这涉及到会增加延迟的线程切换(被诅咒的DoEvents 也是如此)。

标签: c# stream webclient


【解决方案1】:

您只能从文件中获取最后一个 iSize 字节,因为您在每次迭代时都会覆盖缓冲区,您不会将缓冲区保存在任何地方。这是有关如何使用 MemoryStream 将文件存储在内存中的示例。

var totalBytes = new MemoryStream(1024 * 1024);
while ((iByteSize = streamRemote.Read(byteBuffer, 0, iByteSize)) > 0)
{
    totalBytes.Write(byteBuffer, 0, iByteSize);
    iRunningByteTotal += iByteSize;

    //Some progress calculation
    if (Progress != null) Progress(iProgressPercentage);
}

整个下载完成后,就可以转成文字了。

var byteArray = totalBytes.GetBuffer();
var numberOfBytes = totalBytes.Length;
var text = Encoding.Default.GetString(byteArray, 0, numberOfBytes);

更新:DownloadStringAsync 方法与上述基本相同,但不会给您任何进度指示。还有一些其他异步方法会触发 DownloadProgressChanged 事件。

更新 2:关于响应时间。您是否使用其他工具定时下载资源?主要的浏览器都内置了对此类计时的支持。

此外,它是您提供的静态文件还是在服务器端生成的内容?

想到的第三件事是服务器端缓冲。例如。如果使用 ASP.Net 中的 Response.Buffer 属性,则在整个文件/页面完成服务器端之前,不会向客户端发送任何内容。因此客户端必须等待才能开始下载。

【讨论】:

  • 浏览器在大约 5 秒内加载整个页面。下载字符串异步速度是一样的。但奇怪的是,我最终得到了几个 0 的进度和 100 个。它不像我想象的那样异步。
  • @Kaminari - 我们在这里谈论的文件有多大?如果它们很小,那么一次为大家提供服务可能会更有效率,而不是将下载切成小块。
  • @Peter Lillevold 这不是文件。这是一个网站。但只有html没有其他元素。大小约为 1,33MB。
  • @Kaminari - 我明白了。您在问题中特别提到了“文件”,但是,是的,这进一步证明了我的理论,即服务器在构建 html 时正在缓冲,因此不会立即开始向客户端刷新。从我坐的地方,我得到 2.5 秒的延迟,而纯下载只有 0.5 秒,这可能是缓冲的指示。
  • @Peter Lillevold 是的,这是一个问题,不幸的是我无法避免它。但我知道为什么下载时间慢了 2 倍,请查看我的解决方案的第一篇文章。
【解决方案2】:

我对双重阅读感到非常困惑,但它看起来你实际上打算做这样的事情:

        StringBuilder sb = new StringBuilder();           
        using (StreamReader reader = new StreamReader(streamRemote))
        {
            char[] charBuffer = new char[bufferSize];
            int charsRead;
            while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0)
            {
                sb.Append(charBuffer, 0, charsRead);
                //Some progress calculation

                if (Progress != null) Progress(iProgressPercentage);
            }
        }
        string result = sb.ToString();

看看这是否按预期工作.. 但是,我想知道 Progress 是否不是导致下降的原因;在没有分配这个的情况下尝试它,看看这是否使它更快。或者只定期运行:

            //[snip]
            int iteration = 0, charsRead;
            while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0)
            {
                sb.Append(charBuffer, 0, charsRead);
                //Some progress calculation
                if((++iteration % 20) == 0 && Progress != null) {
                    Progress(iProgressPercentage);
                }
            }
            //[snip]

另外,尝试增加缓冲区大小。

【讨论】:

  • 但是为什么打开流需要与网络浏览器下载整个页面相同的时间?
猜你喜欢
  • 2019-10-06
  • 1970-01-01
  • 1970-01-01
  • 2018-01-22
  • 2015-01-16
  • 1970-01-01
  • 2011-01-06
  • 2017-06-09
  • 2014-09-12
相关资源
最近更新 更多