Stream.CopyToAsyn c пуст после первой итерации - PullRequest
0 голосов
/ 20 марта 2020
  • Справочная информация: Мне нужно передать содержимое запроса на несколько других серверов (через client.SendAsync(request)).
  • Проблема: После первого запросить поток контента пуст
[HttpPost]
public async Task<IActionResult> PostAsync() {

    for (var n = 0; n <= 1; n++) {
        using (var stream = new MemoryStream()) {
            await Request.Body.CopyToAsync(stream);
            // why is stream.length == 0 in the second iteration? 
        }
    } 

    return StatusCode((int)HttpStatusCode.OK);
}

Ответы [ 2 ]

1 голос
/ 20 марта 2020

Я обнаружил, что функция CopyToAsync устанавливает исходную позицию потока в последнюю позицию чтения. В следующий раз, когда я использую CopyToAsync, поток начинает читать с последней позиции чтения и не находит больше контента. Однако я не мог использовать Request.Body.Position = 0, поскольку он не поддерживается. Я закончил копированием потока еще раз и сбрасывал позицию после каждой копии.

Если кто-то знает более чистое решение, вы можете указать его.

using (var contentStream = new MemoryStream()) {
    await Request.Body.CopyToAsync(contentStream);                 

    for (var n = 0; n <= 1; n++) {
        using (var stream = new MemoryStream()) {
            contentStream.Position = 0;
            await contentStream.CopyToAsync(stream);

            // works
        }
    }
}
1 голос
/ 20 марта 2020

Потоки имеют указатель, указывающий, в каком положении находится поток; после копирования указатель находится в конце. Вам нужно перемотать поток, установив его положение на 0.

Однако это поддерживается только в потоках, которые поддерживают поиск. Вы можете прочитать поток запроса только один раз . Это связано с тем, что он читается «с провода» и поэтому не поддерживает поиск.

Когда вы хотите скопировать поток запроса в несколько потоков вывода, у вас есть два варианта:

  • Вперед, пока вы читаете
  • Прочитайте один раз в память, а затем переведите по желанию

Первый вариант означает, что все пересылки происходят с одинаковой скоростью; вся передача идет так же медленно, как ввод, или так медленно, как самый медленный читатель. Вы читаете чанк от вызывающего и перенаправляете этот чанк на все адреса переадресации.

При втором подходе вы захотите оценить, можете ли вы хранить все тело запроса плюс тело для каждого адреса переадресации в Память. Если это не является проблемой и правильно настроено с разумными ограничениями, просто скопируйте поток запроса в один MemoryStream и скопируйте и перемотайте его после каждого вызова:

using (var bodyStream = new MemoryStream()) 
{
    await Request.Body.CopyToAsync(bodyStream);

    for (...) 
    {
        using (var stream = new MemoryStream()) 
        {
            await bodyStream.CopyToAsync(stream);
            // Rewind for next copy
            bodyStream.Position = 0;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...