Укажите InputStream для ServletResponce вместо копирования InputStream в OutputStream - PullRequest
0 голосов
/ 13 марта 2012

Короче, у меня есть Сервлет , который загружает фотографии / видео и т. Д. из основного хранилища данных.

Для архивации мне нужно скопировать файлы InputStream в ServletResponce * OutputStream *

С моей точки зрения, это неэффективно, так как перед отправкой мне нужно будет скопировать файл в памяти, было бы удобнее указать InputStream , из которого OutputStream будет читать данные и сразу же отправлять их после чтения некоторых данных в буфере.

Я посмотрел документацию ServletResponce и в ней есть некоторый буфер для данных сообщений, поэтому у меня есть несколько вопросов по этому поводу.

Это правильный механизм? Что если я решу не отправлять файл в конце обработки сервлета? Например: Если бы я скопировал InputStream в OutputStream , а затем обнаружил, что это не авторизованный запрос, и пользователь не имеет права видеть этот объект (возможно, ошибка в дизайне), я бы все равно отправил некоторые данные клиенту, хотя это не то, что я намеревался, или нет.

Ответы [ 2 ]

1 голос
/ 13 марта 2012

Для решения вашей первой проблемы вы можете легко скопировать InputStream в OutputStream, используя IOUtils из Apache Commons Lang:

IOUtils.copy(fileInputStream, servletOutputStream);

Используется буфер 4K, поэтому потребление памяти не должно вызывать беспокойства. На самом деле вы не можете просто отправить данные прямо из InputStream. На самом низком уровне операционная система по-прежнему должна считывать содержимое файла в какую-либо область памяти, и для отправки его в сокет вам необходимо указать область памяти, в которой находятся данные, подлежащие отправке. Потоки - это просто полезная абстракция.

О вашем втором вопросе: вот как работает HTTP: если вы начинаете потоковую передачу данных клиенту, контейнер сервлета сначала отправляет все заголовки ответа. Если вы прервете в середине, с точки зрения клиента это выглядит как прерванная загрузка.

0 голосов
/ 13 марта 2012

Это правильный механизм?

По сути, это механизм only , предоставляемый Servlet APIs.Вы должны сконструировать свой сервлет с учетом этого.

(Трудно понять, как это можно сделать любым другим способом. Системный вызов read считывает данные в память с устройства(диск). Системный вызов write записывает данные из памяти на устройство (сетевой интерфейс). Системного вызова для передачи данных непосредственно с одного устройства на другое не существует. Лучшее, что вы можете сделать, - это уменьшить объем копированияданные в приложении . Если вы используете что-то вроде IOUtils.copy, это должно сводить это к минимуму, насколько это возможно. Единственный способ избежать использования памяти приложения - это использовать какое-то специальное оборудование / операционную системуКомбинация оптимизирована для доставки контента.)

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

Если это НЕ спорныйтогда практическим способом доставки контента будет использование отдельного веб-сервера, реализованного в собственном коде, который мы оптимизировали для доставки статического контента;например, что-то вроде nginx.)

Что, если я решу не отправлять файл в конце обработки Servlet?Например: если я скопировал InputStream в OutputStream, а затем выяснил, что это не авторизованный запрос, и пользователь не имеет права видеть этот объект (возможно, ошибка в дизайне), я все равно отправил бы некоторые данные клиенту, хотя это не то, что я намеревался или нет.

Вы должны написать свой сервлет для проверки доступа ПЕРЕД чтением содержимого в память.И в идеале, перед тем как «зафиксировать» ответ, отправив заголовок ответа.

...