HttpClient ReadAsStringAsync 有进展
HttpClient ReadAsStringAsync with progress
有没有办法获取ReadAsStringAsync()
方法的进度?
我正在获取网站的 HTML 内容并进行解析。
public static async Task<returnType> GetStartup(string url = "http://")
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent",
"Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko");
using (HttpResponseMessage response = await client.GetAsync(url))
{
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
}
}
}
}
Is there a way to get the progress of the ReadAsStringAsync()
method? I am just getting the html content of a website and parsing.
是也不是。
HttpClient
不会从底层 network-stack 公开时间和进度信息,但您 可以 使用 HttpCompletionOption.ResponseHeadersRead
获取一些信息,Content-Length
header,并用自己的 StreamReader
(当然是异步的)自己阅读响应。
请注意,响应 header 中的 Content-Length
将指的是解压前 compressed 内容的长度,而不是原始内容长度,这使事情变得复杂,因为今天可能 most web-servers 将提供 HTML(和静态内容)gzip
压缩(作为 Content-Encoding
或 Transfer-Encoding
), 所以 Content-Length
header 不会告诉你解压内容的长度。不幸的是,虽然 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" }
}
// NOTE: Automatic Decompression is not enabled in this HttpClient so that Content-Length can be safely used. But this will drastically slow down content downloads.
};
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 ); // Note that `capacity` is in 16-bit UTF-16 chars, but responseLength is in bytes, though assuming UTF-8 it evens-out.
Char[] charBuffer = new Char[4096];
while( true )
{
Int32 read = await rdr.ReadAsync( charBuffer ).ConfigureAwait(false);
sb.Append( charBuffer, 0, read );
if( read === 0 )
{
// Reached end.
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
可用,否则您 不应 将 HttpClient
包装在 using
块中,因为这会导致系统资源不足疲惫,尤其是在服务器应用程序中。
- (原因很复杂,也可能因你的 .NET 实现不同而不同(例如我相信 Xamarin 的
HttpClient
没有这个问题),但我不会在这里详述) .
- 因此您可以安全地忽略任何关于不处置您的
HttpClient
的代码分析警告。这是有关始终处置您创建或拥有的任何 IDisposable
object 的规则的少数例外之一。
- 因为
HttpClient
是 thread-safe 并且这是一个 static
方法,请考虑改用缓存的静态实例。
- 您也不需要将
HttpResponseMessage.Content
包装在 using
块中,因为 Content
object 属于 HttpResponseMessage
。
有没有办法获取ReadAsStringAsync()
方法的进度?
我正在获取网站的 HTML 内容并进行解析。
public static async Task<returnType> GetStartup(string url = "http://")
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent",
"Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko");
using (HttpResponseMessage response = await client.GetAsync(url))
{
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
}
}
}
}
Is there a way to get the progress of the
ReadAsStringAsync()
method? I am just getting the html content of a website and parsing.
是也不是。
HttpClient
不会从底层 network-stack 公开时间和进度信息,但您 可以 使用 HttpCompletionOption.ResponseHeadersRead
获取一些信息,Content-Length
header,并用自己的 StreamReader
(当然是异步的)自己阅读响应。
请注意,响应 header 中的 Content-Length
将指的是解压前 compressed 内容的长度,而不是原始内容长度,这使事情变得复杂,因为今天可能 most web-servers 将提供 HTML(和静态内容)gzip
压缩(作为 Content-Encoding
或 Transfer-Encoding
), 所以 Content-Length
header 不会告诉你解压内容的长度。不幸的是,虽然 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" }
}
// NOTE: Automatic Decompression is not enabled in this HttpClient so that Content-Length can be safely used. But this will drastically slow down content downloads.
};
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 ); // Note that `capacity` is in 16-bit UTF-16 chars, but responseLength is in bytes, though assuming UTF-8 it evens-out.
Char[] charBuffer = new Char[4096];
while( true )
{
Int32 read = await rdr.ReadAsync( charBuffer ).ConfigureAwait(false);
sb.Append( charBuffer, 0, read );
if( read === 0 )
{
// Reached end.
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
可用,否则您 不应 将HttpClient
包装在using
块中,因为这会导致系统资源不足疲惫,尤其是在服务器应用程序中。- (原因很复杂,也可能因你的 .NET 实现不同而不同(例如我相信 Xamarin 的
HttpClient
没有这个问题),但我不会在这里详述) . - 因此您可以安全地忽略任何关于不处置您的
HttpClient
的代码分析警告。这是有关始终处置您创建或拥有的任何IDisposable
object 的规则的少数例外之一。 - 因为
HttpClient
是 thread-safe 并且这是一个static
方法,请考虑改用缓存的静态实例。
- (原因很复杂,也可能因你的 .NET 实现不同而不同(例如我相信 Xamarin 的
- 您也不需要将
HttpResponseMessage.Content
包装在using
块中,因为Content
object 属于HttpResponseMessage
。