在内容100%完成之前,从HttpResponseMessage读取标头

时间:2013-03-12 17:30:01

标签: c# asynchronous c#-5.0 dotnet-httpclient

  1. 如何在整个响应流回来之前访问响应标头?
  2. 如何在流式传输时读取流?
  3. HttpClient是接收http响应的粒度控制的最佳选择吗?
  4. 这是一个可以说明我问题的剪辑:

    using (var response = await _httpClient.SendAsync(request,
      HttpCompletionOption.ResponseHeadersRead))
    {
       var streamTask = response.Content.ReadAsStreamAsync();
       //how do I check if headers portion has completed? 
       //Does HttpCompletionOption.ResponseHeadersRead guarantee that?
       //pseudocode
       while (!(all headers have been received)) 
         //maybe await a Delay here to let Headers get fully populated
       access_headers_without_causing_entire_response_to_be_received
    
       //how do I access the response, without causing an await until contents downloaded?
       //pseudocode
       while (stremTask.Resul.?) //i.e. while something is still streaming
         //? what goes here? a chunk-read into a buffer? or line-by-line since it's http?
       ...
    


    编辑为我澄清另一个灰色区域:
    我发掘的任何引用都有某种阻塞语句,会导致等待内容到达。引用我通常读取streamTask.Result或Content上的方法或属性,我不知道我知道足以排除哪些这样的引用是正常的,因为streamTask正在进行而哪些将导致等待直到任务完成。

2 个答案:

答案 0 :(得分:6)

根据我自己的测试,在您开始阅读内容流之前不会传输内容,而且您调用Task.Result是阻塞调用是正确的,但它本质上是一个同步点。 但是,它不会阻止预先缓冲整个内容,它只会阻止内容启动来自服务器。

所以无限的流不会阻塞无限的时间。因此,尝试异步获取流可能被视为过度,特别是如果您的头处理操作相对较短。但是,如果您愿意,您可以随时处理标题,同时在另一个任务上处理内容流。这样的事情可以实现这一点。

static void Main(string[] args)
{
    var url = "http://somesite.com/bigdownloadfile.zip";
    var client = new HttpClient();
    var request = new HttpRequestMessage(HttpMethod.Get, url);

    var getTask = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
    Task contentDownloadTask = null;

    var continuation = getTask.ContinueWith((t) =>
    {
        contentDownloadTask = Task.Run(() =>
        {
            var resultStream = t.Result.Content.ReadAsStreamAsync().Result;
            resultStream.CopyTo(File.Create("output.dat"));
        });

        Console.WriteLine("Got {0} headers", t.Result.Headers.Count());
        Console.WriteLine("Blocking after fetching headers, press any key to continue...");
        Console.ReadKey(true);
    });

    continuation.Wait();
    contentDownloadTask.Wait();
    Console.WriteLine("Finished downloading {0} bytes", new FileInfo("output.dat").Length);

    Console.WriteLine("Finished, press any key to exit");
    Console.ReadKey(true);
}

请注意,无需检查标题部分是否完整,您已使用HttpCompletionOption.ResponseHeadersRead选项明确指定。在检索到标题之前,SendAsync任务不会继续。

答案 1 :(得分:4)

使用await / async关键字的结果更具可读性:

var url = "http://somesite.com/bigdownloadfile.zip";

using (var httpClient = new HttpClient())
using (var httpRequest = new HttpRequestMessage(HttpMethod.Get, url ))
using(HttpResponseMessage response = await httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead))
using (Stream stream = await response.Content.ReadAsStreamAsync())
{
    //Access to the Stream object as it comes, buffer it or do whatever you need
}