Является MVC's ASP.NET FileStreamResult, менее эффективный, чем запись непосредственно в Поток вывода Ответа, или я пропускаю что-то?

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

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

someObjectThatDumpsOutputToWhateverStreamYouHandIt.WriteTo(Response.OutputStream); 

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

Однако это не является очень дружественным по отношению к модульным тестам. На самом деле можно все еще сделать такой код в ASP.NET MVC, но пользовательское должно было бы избежать этого. Скорее ASP.NET MVC поощрил бы Вас возвращать ActionResult. ActionResult является понятием MVC ASP.NET, которое существует главным образом, чтобы быть дружественным модульным тестом. Это позволяет Вам "объявлять" то, что Вы хотите сделанный. Модульный тест может осуществить действие Контроллера и подтвердить, что это получает ожидаемый ActionResult. Тот модульный тест может работать вне браузера.

ASP.NET MVC предлагает своего рода ActionResult для возврата Потоков данных. Это называют FileStreamResult. Не позволяйте слову "File" на имя одурачить Вас. Это о возврате Потока данных, точно поскольку мы говорим о вышеупомянутом.

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

Таким образом, кажется, что я должен сделать что-то вроде этого (намеренно исключение располагают / использующий / и т.д.):

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

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

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

Таким образом, вот мой вопрос: я пропустил что-то, или это с точностью до, говорят, что использование FileStreamResult вынуждает Вас иметь промежуточное место хранения, которое Вы не были бы вынуждены иметь, если бы необходимо было записать непосредственно в поток вывода Ответа?

Спасибо.

9
задан Jonathan Leffler 3 February 2010 в 03:45
поделиться

2 ответа

Я думаю, что было бы точнее сказать, что, если предположить, что какой-нибудь ОbjectThatDumpsOutputToWeverStreamYouHandIt не унаследует от System.IO. Stream, что использование FileStreamResult заставляет вас либо

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

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

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

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

5
ответ дан 4 December 2019 в 13:47
поделиться

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

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

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

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

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


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

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

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

10
ответ дан 4 December 2019 в 13:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: