Является ли FileStreamResult в ASP.NET MVC менее эффективным, чем запись непосредственно в поток вывода ответа, или я что-то упустил? - PullRequest
9 голосов
/ 03 февраля 2010

Прежде всего, я люблю ASP.NET MVC. Этот вопрос не критика этого. Скорее, я хочу подтвердить то, что я вижу, и убедиться, что я что-то не пропустил. Терпите меня ... Я не могу добраться до вопроса, не предоставив немного контекста.

Вопрос связан с возвратом данных в потоке в ответ на сообщение HTTP. В старые времена до ASP.NET MVC вы могли сделать это, отправив свои данные напрямую в поток ответов. Например, вы можете сделать что-то вроде этого:

someObjectThatDumpsOutputToWhateverStreamYouHandIt.WriteTo(Response.OutputStream); 

Обратите внимание на ключевой аспект этого кода: я не реализовал ЛЮБОГО резервного хранилища. Мне не нужно было передавать информацию в байтовый массив или записывать ее во временный файл. Скорее, ASP.NET уже настроил поток для передачи ответа обратно в браузер, и я выгружал нужный вывод прямо в этот поток. Это более эффективно с точки зрения использования процессора, памяти и времени выполнения, чем копирование всех данных во временное местоположение и последующее перемещение их в поток ответов.

Однако он не очень подходит для юнит-тестов. Фактически, вы все еще можете делать такой код в ASP.NET MVC, но обычай будет избегать этого. Скорее ASP.NET MVC будет рекомендовать вам вернуть ActionResult. ActionResult - это концепция ASP.NET MVC, которая в основном предназначена для модульного тестирования. Это позволяет вам «объявить», что вы хотите сделать. Модульный тест может выполнить действие контроллера и подтвердить, что он получает ожидаемый ActionResult. Этот модульный тест может выполняться вне браузера.

ASP.NET MVC предлагает своего рода ActionResult для возврата потоков данных. Это называется FileStreamResult. Не позволяйте слову «Файл» в имени обмануть вас. Речь идет о возврате потока данных, в точности как мы говорим выше.

Однако вот проблема и основа моего вопроса: Если вы заставите свой метод Controller возвращать FileStreamResult, и вы передадите ему поток, который вы хотите вернуть, то, по-видимому, его больше нет чтобы вы выгружали данные непосредственно в поток ответа. Теперь, похоже, вы вынуждены создать свой собственный поток с резервным хранилищем (например, памятью или файлом), сбросить в него свои данные и затем передать Поток к FileStreamResult, который вы возвращаете.

Так что, похоже, я должен сделать что-то вроде этого (намеренно опуская dispose / using / etc.):

MemoryStream myIntermediateStream = new MemoryStream();
someObjectThatDumpsOutputToWhateverStreamYouHandIt.WriteTo(myIntermediateStream ); 

return new FileStreamResult(myIntermediateStream, "application/pdf");

Обратите внимание, что myIntermediateStream заставляет содержимое большого потока данных временно сохраняться в памяти, чтобы впоследствии FileStreamResult мог снова скопировать его в выходной поток Response.

Итак, вот мой вопрос: я что-то упустил или правильно сказать, что использование FileStreamResult вынуждает вас иметь промежуточное хранилище, которое вы не будете иметь, если будете писать прямо в ответ? выходной поток?

Спасибо.

Ответы [ 2 ]

10 голосов
/ 03 февраля 2010

Я думаю, что основная идея FileStreamResult заключается в том, что вы используете его, когда у вас уже есть поток.Если вам нужно создать временный поток, чтобы использовать его, то в этом нет никакого смысла;вот почему есть также FilePathResult и FileContentResult.

. Здесь есть несколько случаев, когда у вас уже может быть поток:

  • Данные хранятся встолбец SQL 2008 FILESTREAM;
  • Данные, пересылаемые напрямую из другой конечной точки (NetworkStream);
  • A GzipStream или DeflateStream для отправки чего-либо сжатого;
  • Отправка из именованного канала (PipeStream);
  • ... и т. Д.

Основным вариантом использования FileStreamResult (насколько я понимаю) является случай, когдау вас есть уже существующий поток, который вам нужно будет прочитать из и затем записать в ответ;вместо того, чтобы выполнять чтение, запись и вычисление буферизации самостоятельно, FileStreamResult обрабатывает ее для вас.

Так что короткий ответ - нет, FileStreamResult не обязательно заставляет вас иметь«промежуточное» хранилище, если потоком являются ваши данные source .Я не стал бы беспокоиться о FileStreamResult, если данные уже находятся в памяти или где-то на диске, готовы и ожидают записи непосредственно в поток ответов.


Я просто хотел бы добавить еще одинпояснение: проблема с этой библиотекой заключается в том, что она инвертирует функциональность, которая вам действительно нужна для FileStreamResult.Вместо того, чтобы вручать вам поток, из которого вы можете читать, он ожидает, что вы предоставите поток, чтобы он мог писать в него.Имейте в виду, что это обычная схема, но она не очень подходит для этой задачи.

Если поток содержит много данных и вы не хотите проверять память с ним, вам следуетвозможность инвертировать поток самостоятельно, используя производные PipeStream .Создайте именованный или анонимный канал, создайте поток сервера (который вы можете инициализировать с помощью любого размера буфера, который вам нужен) и передайте его в библиотеку, затем создайте поток клиента в том же канале и направьте его в FileStreamResult.

Это дает вам тестируемость, которую вы хотите, и позволяет точно контролировать, сколько памяти разрешено использовать процессу.Он также имеет дополнительное преимущество, заключающееся в возможности асинхронного производства данных, что может потребоваться, если вы пытаетесь выделить гигабайты данных.

5 голосов
/ 03 февраля 2010

Полагаю, было бы точнее сказать, что если предположить, что someObjectThatDumpsOutputToWhwhatStreamYouHandIt не наследуется от System.IO.Stream, то при использовании FileStreamResult вы либо

a) реализовать обертку для некоторого объекта ObjectThatDumpsOutputToWhwhatStreamYouHandIt, который наследуется от System.IO.Stream, или

б) использовать временный экземпляр MemoryStream.

Таким образом, он не обязательно менее эффективен, но, по-видимому, требует немного больше усилий для возврата значения.

Редактировать с помощью вопроса Автор вопроса: Я выбираю этот вопрос в качестве принятого ответа. Оба ответа были очень полезны. Я должен выбрать один, и этот указывает мне на DelegatingActionResult Фила Хаака, который именно то, что мне нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...