Как прочитать один и тот же входной поток дважды? Можно ли как-нибудь скопировать?
Мне нужно получить изображение из Интернета, сохранить его локально, а затем вернуть сохраненное изображение. Я просто подумал, что было бы быстрее использовать тот же поток вместо того, чтобы запускать новый поток для загруженного контента, а затем читать его снова.
java
inputstream
Warpzit
источник
источник
Ответы:
Вы можете использовать
org.apache.commons.io.IOUtils.copy
для копирования содержимого InputStream в байтовый массив, а затем многократно читать из байтового массива с помощью ByteArrayInputStream. Например:источник
В зависимости от того, откуда идет InputStream, вы не сможете его сбросить. Вы можете проверить , поддерживаются ли
mark()
иreset()
поддерживаются, используяmarkSupported()
.Если это так, вы можете вызвать
reset()
InputStream, чтобы вернуться к началу. Если нет, вам нужно снова прочитать InputStream из источника.источник
InputStream
@ayahuasca вродеBufferedInputStream
поддерживают 'mark'если вы
InputStream
поддерживаете использование mark, вы можете использоватьmark()
свой inputStream, а затемreset()
его. если вашInputStrem
знак поддержки не поддерживается, вы можете использовать классjava.io.BufferedInputStream
, чтобы вы могли встроить свой поток вBufferedInputStream
такойисточник
BufferedInputStream.fill()
, есть раздел «буфер роста», где новый размер буфера сравнивается только сmarklimit
иMAX_BUFFER_SIZE
.Вы можете обернуть входной поток с помощью PushbackInputStream. PushbackInputStream позволяет непрочитанных ( « обратной записи ») байтов , которые уже были прочитаны, так что вы можете сделать так:
Обратите внимание, что PushbackInputStream хранит внутренний буфер байтов, поэтому он действительно создает буфер в памяти, который содержит байты, «записанные обратно».
Зная этот подход, мы можем пойти дальше и объединить его с FilterInputStream. FilterInputStream сохраняет исходный входной поток в качестве делегата. Это позволяет создать новое определение класса, которое позволяет автоматически « непрочитать » исходные данные. Определение этого класса следующее:
У этого класса есть два метода. Один для чтения в существующий буфер (определение аналогично вызову
public int read(byte b[], int off, int len)
класса InputStream). Второй, который возвращает новый буфер (это может быть более эффективным, если размер буфера для чтения неизвестен).Теперь посмотрим на наш класс в действии:
источник
Если вы используете реализацию
InputStream
, вы можете проверить результат,InputStream#markSupported()
который скажет вам, можете ли вы использовать методmark()
/reset()
.Если вы можете отметить поток при чтении, то позвоните,
reset()
чтобы вернуться, чтобы начать.Если вы не можете, вам придется снова открывать поток.
Другим решением было бы преобразовать InputStream в массив байтов, а затем перебирать массив столько раз, сколько вам нужно. В этом посте вы можете найти несколько решений. Преобразовать InputStream в байтовый массив на Java, используя сторонние библиотеки или нет. Осторожно, если прочитанное содержимое слишком велико, могут возникнуть проблемы с памятью.
Наконец, если вам нужно прочитать изображение, используйте:
Использование
ImageIO#read(java.net.URL)
также позволяет использовать кеш.источник
ImageIO#read(java.net.URL)
: некоторые веб-серверы и сети CDN могут отклонять простые вызовы (т. е. без пользовательского агента, который заставляет сервер полагать, что вызов исходит от веб-браузера), сделанныхImageIO#read
. В этом случаеURLConnection.openConnection()
установка пользовательского агента на это соединение + использование ImageIO.read (InputStream) в большинстве случаев поможет.InputStream
это не интерфейсКак насчет:
источник
Для разделения на
InputStream
две части, избегая загрузки всех данных в память , а затем обрабатывать их независимо:OutputStream
, а именно:PipedOutputStream
PipedInputStream
возвращаютсяInputStream
.OutputStream
. Итак, все, что было прочитано из источникаInputStream
, будет написано на обоихOutputStream
. Нет необходимости реализовывать это, потому что это уже сделано вTeeInputStream
(commons.io).Внутри отдельного потока считайте весь исходный inputStream, и неявно входные данные передаются в целевые inputStreams.
Не забывайте закрывать inputStreams после использования и закрывать выполняющийся поток:
TeeInputStream.readAllBytes()
В случае, если вам нужно разделить его на несколько
InputStream
, а не на два. Замените в предыдущем фрагменте кода классTeeOutputStream
для вашей собственной реализации, которая инкапсулируетList<OutputStream>
и переопределяетOutputStream
интерфейс:источник
Преобразуйте входной поток в байты, а затем передайте его функции savefile, где вы собираете то же самое во входной поток. Также в исходной функции используйте байты для других задач.
источник
В случае, если кто-то работает в приложении Spring Boot, и вы хотите прочитать тело ответа
RestTemplate
(вот почему я хочу прочитать поток дважды), есть чистый (эр) способ сделать это.Прежде всего, вам нужно использовать Spring
StreamUtils
для копирования потока в String:Но это не все. Вам также необходимо использовать фабрику запросов, которая может буферизовать поток для вас, например:
Или, если вы используете фабричный компонент, то (это Kotlin, но тем не менее):
Источник: https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/
источник
Если вы используете RestTemplate для HTTP-вызовов, просто добавьте перехватчик. Тело ответа кэшируется реализацией ClientHttpResponse. Теперь inputstream может быть извлечен из respose столько раз, сколько нам нужно.
источник