有没有一种方法可以获取
ReadAsStringAsync()
方法的进度? 我只是在获取网站的HTML内容并解析它。
是和不是。
HttpClient 不会从底层网络栈中公开计时和进度信息,但是您可以通过使用 HttpCompletionOption.ResponseHeadersRead、Content-Length 标头和使用您自己的 StreamReader(当然是异步的)读取响应来获取一些信息。
请注意,响应标头中的 Content-Length 将指向解压缩之前压缩的内容长度,而不是原始内容长度,这使事情变得复杂,因为今天可能大多数 Web 服务器都会使用 gzip 压缩(作为 Content-Encoding 或 Transfer-Encoding)提供 HTML(和静态内容),因此 Content-Length 标头将无法告诉您解压缩后内容的长度。不幸的是,虽然 HttpClient 可以为您执行自动 GZip 解压缩,但它不会告诉您解压缩后的内容长度。
但是,您仍然可以向方法的消费者报告某些类型的进度,下面是一个示例。 您应该使用.NET惯用的 IProgress<T> 接口而不是自己编写。
像这样:
private static readonly HttpClient _hc = new HttpClient()
{
DefaultRequestHeaders =
{
{ "User-Agent", "Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko" }
}
};
public static async Task<T> GetStartupAsync( IProgress<String> progress, string url = "http://")
{
progress.Report( "Now making HTTP request..." );
using( HttpResponseMessage response = await client.GetAsync( url, HttpCompletionOption.ResponseHeadersRead ) )
{
progress.Report( "Received HTTP response. Now reading response content..." );
Int64? responseLength = response.Content.Headers.ContentLength;
if( responseLength.HasValue )
{
using( Stream responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false) )
using( StreamReader rdr = new StreamReader( responseStream ) )
{
Int64 totalBytesRead = 0;
StringBuilder sb = new StringBuilder( capacity: responseLength.Value );
Char[] charBuffer = new Char[4096];
while( true )
{
Int32 read = await rdr.ReadAsync( charBuffer ).ConfigureAwait(false);
sb.Append( charBuffer, 0, read );
if( read === 0 )
{
progress.Report( "Finished reading response content." );
break;
}
else
{
progress.Report( String.Format( CultureInfo.CurrentCulture, "Read {0:N0} / {1:N0} chars (or bytes).", sb.Length, resposneLength.Value );
}
}
}
}
else
{
progress.Report( "No Content-Length header in response. Will read response until EOF." );
string result = await content.ReadAsStringAsync();
}
progress.Report( "Finished reading response content." );
}
注意:
- 通常,任何
async
方法或返回Task
/Task<T>
的方法都应该以Async
为后缀命名,所以您的方法应该命名为GetStartupAsync
,而不是GetStartup
。
- 除非您有一个可用的
IHttpClientFactory
,否则您不应该在using
块中包装HttpClient
,因为这可能会导致系统资源枯竭,特别是在服务器应用程序中。
- (造成这种问题的原因很复杂,并且也可能因您的.NET实现而异(例如,我相信Xamarin的
HttpClient
没有这个问题),但我不会在此讨论细节)。
- 因此,您可以安全地忽略任何有关未处置
HttpClient
的代码分析警告。这是始终处置您创建或拥有的任何IDisposable
对象规则的少数例外之一。
- 由于
HttpClient
是线程安全的,这是一个static
方法,因此考虑使用已缓存的静态实例。
- 您也不需要在
HttpResponseMessage.Content
上包装using
块,因为Content
对象是由HttpResponseMessage
所拥有的。
String
读取。 - DaiContent-Length
头部,就无法指示任何百分比进度。 - DaiContent-Length
是必需的,没有它你就完了。 - Dai