使用HttpClient阻止下载整个远程资源?

时间:2017-06-06 19:52:40

标签: http uwp httpclient

在UWP下,低级HttpWebRequest类及其同类已被弃用,the official recommendation将使用System.Net.Http.HttpClient

但是,HttpClient实现中似乎存在明显的疏忽或错误:API似乎没有提供对远程URI发出请求的方法,该远程URI不会自动下载整个远程资源(在GET请求的情况下)允许对响应流进行惰性评估。

The documentation for the HttpClient.GetAsync() method说:

  

此操作不会阻止。在读取完整个响应(包括内容)后,返回的任务对象将完成。

在认为完成请求之前,会提前说明将下载整个远程资源。如果指定HttpCompletionOption,理论上允许解决此问题的HttpCompletionOption.ResponseHeadersRead参数,如下所示:

  

一旦响应可用并且读取标题,操作就应该完成。内容尚未阅读。

但是,无论指定了哪个HttpCompletionOption,在所有情况下,HttpClient.GetAsync()似乎都会分配响应的完整ContentLength字节(在内存中!一次性!没有任何限制)检查!)在这个过程中。当然,这有点疯狂,是一个真正的问题。

在我的特定情况下,我只想从不支持http范围标头的服务器读取数千兆字节远程资源的前几个kb。这通常是一种“无汗”的操作:只需创建Web请求,从响应流中读取,直到您满意为止,然后关闭响应并以愉快的方式进行。

这似乎不是默认HttpClient API的选项。是否有一个简单的解决方法,不涉及使用原始套接字制作我自己的HTTP请求?

1 个答案:

答案 0 :(得分:1)

  

在所有情况下,HttpClient.GetAsync()似乎分配响应的完整ContentLength字节(在内存中!一次性!没有任何限制检查!)

HttpCompletionOption.ResponseHeadersRead选项不适用。

就像使用旧的sweet HttpWebRequest类一样,您可以打开响应流并读取几个字节,然后丢弃响应。这是an example

这是我的实验:(我使用的是Windows.Web.Http.HttpClient,但System.Net.Http.HttpClient提供了类似的API)

private HttpClient httpClient = new HttpClient();
private CancellationTokenSource cts = new CancellationTokenSource();


private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    Uri resourceAddress = new Uri("http://somewhere/gigabyte.zip");

    try
    {
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, resourceAddress);

        // Do not buffer the response.
        HttpResponseMessage response = await httpClient.SendRequestAsync(
            request,
            HttpCompletionOption.ResponseHeadersRead).AsTask(cts.Token);

        using (Stream responseStream = (await response.Content.ReadAsInputStreamAsync()).AsStreamForRead())
        {
            int read = 0;
            byte[] responseBytes = new byte[1000];
            do
            {
                read = await responseStream.ReadAsync(responseBytes, 0, responseBytes.Length);
                break;
            } while (read != 0);
        }                
    }
    catch (TaskCanceledException)
    {                
    }
    catch (Exception ex)
    {   
    }
    finally
    { 
    }
}