如何使用进度回调将文件下载到字符串中?

时间:2010-04-26 11:45:12

标签: c# stream webclient

我想使用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;
    }

我认为asyncro功能几乎是一样的。但我将简单地使用另一个线程来激活此功能。我不需要percise进度指示。

2 个答案:

答案 0 :(得分:1)

我对双读非常困惑,但看起来就像你实际打算做的那样:

        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]

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

答案 1 :(得分: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属性,则在服务器端完成整个文件/页面之前,不会向客户端发送任何内容。因此,客户端必须等待才能开始下载。