【问题标题】:HTTP Request with multiple Ranges具有多个范围的 HTTP 请求
【发布时间】:2011-01-18 14:30:59
【问题描述】:

如果我想部分下载文件并在请求标头中定义单个范围,我会得到 响应正文中请求文件的字节流。

但如果我指定多个范围如下,我总是会为每个定义的范围额外 响应正文中损坏的响应标头(描述请求的范围) 下载的文件。

static void Main(string[] args)
{

    Console.Write("Please enter target File: ");
    string Target = Console.ReadLine();
    string Source = @"http://mozilla-mirror.3347.voxcdn.com/pub/mozilla.org/firefox/releases/3.6/win32/de/Firefox%20Setup%203.6.exe";

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Source);
    request.Credentials = CredentialCache.DefaultCredentials;

    // define multiple Ranges
    request.AddRange(      0, 1000000);
    request.AddRange(1000000, 2000000);

    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

    using (Stream source = response.GetResponseStream())
    {
        using (FileStream target = File.Open(Target, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
        {
            byte[] buffer = new byte[4096];
            int BytesRead = 0;
            int TotalBytesRead = 0;

            while((BytesRead = source.Read(buffer, 0, buffer.Length)) > 0) 
            {
                target.Write(buffer, 0, BytesRead);
                TotalBytesRead += BytesRead;

                Console.WriteLine("{0}", TotalBytesRead);
            }
        }
    }

    Console.WriteLine("Downloading Finished!");
    Console.ReadLine();
}

请求如W​​ireshark所示:

http://img197.imageshack.us/img197/8199/requesty.png

Response Body 应该只包含文件的 Byte-Stream,但在每个定义 Range 的开头额外包含不需要的 Response-Header:

是否可以在不单独请求每个 Range 的情况下避免正文中额外的响应头?

是否有内置方法来过滤附加响应标头,其大小可能因 HTTP 服务器而异?

【问题讨论】:

  • 只是问:你为什么要这样做?我猜你上面的代码只是一个例子,因为像这样直接请求连续的范围是没有意义的?

标签: c# .net http httpwebrequest


【解决方案1】:

不,这就是 HTTP/1.1 中多个范围的工作方式。见RFC 2616, Section 19.2

【讨论】:

    【解决方案2】:

    感谢您的帮助,如上面链接中所述,它是假定的 http 响应具有多个范围的请求的方式。

    所以....

    是否可以避免在正文中添加额外的响应头而不 分别请求每个范围?

    => 没有。

    是否有内置方法来过滤附加响应标头,其大小 可能因 HTTP 服务器而异?

    => 我不知道,但是...

    也许你们中的一些人可能会对以下代码块有一个批判性的看法 从文件数据中过滤标题:

    public void DoDownload(Range[] Ranges)
        {
    
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_Source);
            request.Credentials = CredentialCache.DefaultCredentials;
    
            foreach (Range r in Ranges)
            {
                request.AddRange(r.From, r.To);
            }
    
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    
            string boundary = "";
            Match m = Regex.Match(response.ContentType, @"^.*boundary=(?<boundary>.*)$");
    
            if (m.Success)
            {
                boundary = m.Groups["boundary"].Value;
            }
            else
            {
                throw new InvalidDataException("invalid packet data: no boundary specification found.");
            }
    
    
            using (Stream source = response.GetResponseStream())
            {
                using (FileStream target = File.Open(m_TargetFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
                {
                    // buffer for payload
                    byte[] buffer = new byte[4096];
                    // buffer for current range header
                    byte[] header = new byte[200];
                    // next header after x bytes
                    int NextHeader = 0;
                    // current position in header[]
                    int HeaderPosition = 0;
                    // current position in buffer[]
                    int BufferPosition = 0;
                    // left data to proceed
                    int BytesToProceed = 0;
                    // total data written without range-headers
                    long TotalBytesWritten = 0;
                    // count of last data written to target file
                    int BytesWritten = 0;
                    // size of processed header data
                    int HeaderSize = 0;
                    // count of last data read from ResponseStream
                    int BytesRead = 0;
    
    
                    while ((BytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        BufferPosition = 0;
                        BytesToProceed = BytesRead;
                        HeaderSize = 0;
    
                        while (BytesToProceed > 0)
                        {
                            if (NextHeader == 0)
                            {
                                for (;HeaderPosition < header.Length; HeaderPosition++, BufferPosition++, HeaderSize++)
                                {
                                    if (BytesToProceed > HeaderPosition && BufferPosition < BytesRead)
                                    {
                                        header[HeaderPosition] = buffer[BufferPosition];
    
                                        if (HeaderPosition >= 4 &&
                                            header[HeaderPosition - 3] == 0x0d &&
                                            header[HeaderPosition - 2] == 0x0a &&
                                            header[HeaderPosition - 1] == 0x0d &&
                                            header[HeaderPosition] == 0x0a)
                                        {
                                            string RangeHeader = Encoding.ASCII.GetString(header, 0, HeaderPosition + 1);
                                            Match mm = Regex.Match(RangeHeader,
                                                @"^\r\n(--)?" + boundary + @".*?(?<from>\d+)\s*-\s*(?<to>\d+)/.*\r\n\r\n", RegexOptions.Singleline);
    
                                            if (mm.Success)
                                            {
                                                int RangeStart = Convert.ToInt32(mm.Groups["from"].Value);
                                                int RangeEnd = Convert.ToInt32(mm.Groups["to"].Value);
    
                                                NextHeader = (RangeEnd - RangeStart) + 1; 
    
                                                target.Seek(RangeStart, SeekOrigin.Begin);
                                                BufferPosition++;
    
                                                BytesToProceed -= HeaderSize + 1;
    
                                                HeaderPosition = 0;
                                                HeaderSize = 0;
    
                                                break;
                                            }
                                            else { throw new InvalidDataException("invalid header: missing range specification.");}
                                        }
                                    }
                                    else { goto READ_NEW; }
                                }
    
                                if (NextHeader == 0)
                                    throw new InvalidDataException("invalid packet data: no range-header found.");
                            }
    
                            BytesWritten = (NextHeader > BytesToProceed) ? BytesToProceed : NextHeader;
    
                            target.Write(buffer, BufferPosition, BytesWritten);
    
                            BytesToProceed -= BytesWritten;
                            NextHeader -= BytesWritten;
                            BufferPosition += BytesWritten;
    
                            TotalBytesWritten += BytesWritten;
                        }
    
                    READ_NEW:;
                    }
                }
            }
        }
    

    如果有另一种/更好的方法可以给我一些提示。

    最好的问候 cap_Chap

    【讨论】:

    • 看起来很吓人。尽管似乎没有任何支持多部分响应的 C# HTTP 客户端库(可能是因为它们很少使用)。或者也许我看起来不是很努力。无论如何,如果您被困在替代方案中,为什么不使用单个范围发出多个 HTTP 请求?
    • 稍后,但应该仍然是一个很好的参考。 WebAPI 客户端库支持多部分请求c-sharpcorner.com/article/…
    猜你喜欢
    • 2015-02-04
    • 2012-03-07
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2013-09-04
    • 2022-12-10
    • 1970-01-01
    • 2013-01-25
    相关资源
    最近更新 更多