#c# #asynchronous #async-await #stream #httpclient
#c# #асинхронный #асинхронный-ожидание #поток #httpclient
Вопрос:
Есть ли способ получить прогресс 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();
}
}
}
}
Комментарии:
1. Какой длины строка? Если размер ответа достаточно велик, чтобы гарантировать информацию о ходе выполнения (т. Е. Размером более нескольких мегабайт), то вам, вероятно, не следует читать его как
String
.2. Кроме того, какая именно информация о ходе выполнения вам нужна? Для ответа размером менее ~ 10 КБ (т. Е. Пары TCP-пакетов / ethernet-кадров) невозможно получить числовой показатель прогресса в процентах, потому что он будет прыгать с 0% до 100% за один раз.
3. @Dai размер строки составляет от 3 МБ до 10 МБ
4. Без
Content-Length
заголовка невозможно указать какой-либо процент прогресса.5. Ни один из них не имеет отношения к делу.
Content-Length
требуется, без него вы SOL.
Ответ №1:
Есть ли способ получить прогресс
ReadAsStringAsync()
метода? Я просто получаю HTML-содержимое веб-сайта и анализирую.
Да и нет.
HttpClient
не предоставляет информацию о времени и ходе выполнения из базового сетевого стека, но вы можете получить некоторую информацию, используя HttpCompletionOption.ResponseHeadersRead
Content-Length
заголовок и самостоятельно прочитав ответ самостоятельно StreamReader
(конечно, асинхронно).
Обратите внимание, что Content-Length
в заголовках ответа будет указываться длина сжатого содержимого до распаковки, а не длина исходного содержимого, что усложняет ситуацию, потому что, вероятно, большинство веб-серверов сегодня будут обслуживать HTML (и статический контент) со gzip
сжатием (либо Content-Encoding
или Transfer-Encoding
), поэтому Content-Length
заголовок вам не скажетдлина распакованного содержимого. К сожалению, хотя вы HttpClient
можете выполнить автоматическую распаковку GZip, она не сообщит вам, какова длина распакованного содержимого.
Но вы все равно можете сообщать о некоторых видах прогресса обратно потребителю вашего метода, см. Пример ниже. Вы должны сделать это с помощью .ЧИСТЫЙ идиоматический 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
доступного, вы не должны заключать aHttpClient
вusing
блок, потому что это может привести к исчерпанию системных ресурсов, особенно в серверном приложении.- (Причины этого сложны и также могут отличаться в зависимости от вашего .СЕТЕВАЯ реализация (например, я считаю, что у Xamarin
HttpClient
нет этой проблемы), но я не буду вдаваться в подробности здесь). - Таким образом, вы можете безопасно игнорировать любое предупреждение анализа кода о том, что вы не утилизируете ваш
HttpClient
. Это одно из немногих исключений из правила о постоянном удалении любыхIDisposable
объектов, которые вы создаете или которыми владеете. - Поскольку
HttpClient
это потокобезопасный и этоstatic
метод, рассмотрите возможность использования вместо него кэшированного статического экземпляра.
- (Причины этого сложны и также могут отличаться в зависимости от вашего .СЕТЕВАЯ реализация (например, я считаю, что у Xamarin
- Вам также не нужно заключать
HttpResponseMessage.Content
вusing
блок, посколькуContent
объект принадлежитHttpResponseMessage
.
Комментарии:
1. Как я уже сказал в комментарии выше, доступна длина содержимого. Я думаю, я должен пойти на ReadAsStreamAsync?
2. @Alejandro Я обновил свой ответ, чтобы учесть
Content-Length
.3. У вас слишком много ошибок с опечатками. Я прояснил это, но вы также должны сделать это для ответа
4. @Alejandro Код в моем ответе предназначен только в качестве иллюстративного примера и не предназначен для копирования и вставки в производство. Вы никогда не должны слепо копировать и вставлять код из StackOverflow — или любого другого веб-сайта, если на то пошло.
5. Я не говорю обо мне, я говорю о будущих ссылках. На самом деле мне не нужна была иллюстрация ReadAsStringAsync, мой вопрос касался ReadAsStringAsync, а не streamer. Я принимаю ответ, поскольку выясняю, что с ReadAsStringAsync нет решения, кроме использования streamer. В любом случае, как вы пожелаете