Jak odczytujesz dwa razy ten sam strumień wejściowy? Czy da się to jakoś skopiować?
Muszę pobrać obraz z Internetu, zapisać go lokalnie, a następnie zwrócić zapisany obraz. Po prostu pomyślałem, że szybsze będzie użycie tego samego strumienia zamiast rozpoczynania nowego strumienia do pobranej zawartości, a następnie przeczytania go ponownie.
java
inputstream
Warpzit
źródło
źródło
Odpowiedzi:
Możesz użyć,
org.apache.commons.io.IOUtils.copy
aby skopiować zawartość InputStream do tablicy bajtów, a następnie wielokrotnie czytać z tablicy bajtów przy użyciu ByteArrayInputStream. Na przykład:źródło
W zależności od tego, skąd pochodzi InputStream, możesz nie być w stanie go zresetować. Możesz sprawdzić, czy
mark()
i czyreset()
są obsługiwane przy użyciumarkSupported()
.Jeśli tak jest, możesz wywołać
reset()
InputStream, aby powrócić do początku. Jeśli nie, musisz ponownie odczytać InputStream ze źródła.źródło
InputStream
podklasy, takie jakBufferedInputStream
obsługuje ` ` znak ''jeśli
InputStream
wspierasz używanie znaku, możeszmark()
swój inputStream, a następniereset()
. jeśli twójInputStrem
nie obsługuje znaku, możesz użyć klasyjava.io.BufferedInputStream
, więc możesz osadzić swój strumień wBufferedInputStream
podobnymźródło
BufferedInputStream.fill()
, jest sekcja „powiększ bufor”, w której nowy rozmiar bufora jest porównywany tylko zmarklimit
iMAX_BUFFER_SIZE
.Możesz zawijać strumień wejściowy za pomocą PushbackInputStream. PushbackInputStream umożliwia nieprzeczytanie („ zapis z powrotem ”) bajtów, które zostały już odczytane, więc możesz to zrobić:
Zwróć uwagę, że PushbackInputStream przechowuje wewnętrzny bufor bajtów, więc naprawdę tworzy bufor w pamięci, który przechowuje bajty „zapisane z powrotem”.
Znając to podejście, możemy pójść dalej i połączyć je z FilterInputStream. FilterInputStream przechowuje oryginalny strumień wejściowy jako delegata. Pozwala to na utworzenie nowej definicji klasy, która pozwala na automatyczne „ nieprzeczytanie ” oryginalnych danych. Definicja tej klasy jest następująca:
Ta klasa ma dwie metody. Jeden do odczytu do istniejącego bufora (definicja jest analogiczna do wywołania
public int read(byte b[], int off, int len)
klasy InputStream). Drugi, który zwraca nowy bufor (może to być bardziej efektywne, jeśli rozmiar bufora do odczytu jest nieznany).Zobaczmy teraz naszą klasę w akcji:
źródło
Jeśli korzystasz z implementacji
InputStream
, możesz sprawdzić wynikInputStream#markSupported()
i powiedzieć, czy możesz użyć metodymark()
/reset()
.Jeśli możesz oznaczyć strumień podczas czytania, zadzwoń,
reset()
aby wrócić i rozpocząć.Jeśli nie możesz, będziesz musiał ponownie otworzyć strumień.
Innym rozwiązaniem byłoby przekonwertowanie InputStream na tablicę bajtów, a następnie iterowanie po tablicy tyle razy, ile potrzebujesz. Możesz znaleźć kilka rozwiązań w tym poście Konwertuj InputStream na tablicę bajtów w Javie, używając bibliotek innych firm lub nie. Uwaga, jeśli odczytana zawartość jest zbyt duża, mogą wystąpić problemy z pamięcią.
Na koniec, jeśli potrzebujesz przeczytać obraz, użyj:
Używanie
ImageIO#read(java.net.URL)
pozwala również na użycie pamięci podręcznej.źródło
ImageIO#read(java.net.URL)
: niektóre serwery WWW i CDN mogą odrzucać gołe wywołania (tj. bez agenta użytkownika, który sprawia, że serwer uważa, że wywołanie pochodzi z przeglądarki internetowej) wykonane przezImageIO#read
. W takim przypadkuURLConnection.openConnection()
ustawienie agenta użytkownika na to połączenie + za pomocą `ImageIO.read (InputStream) w większości przypadków załatwi sprawę.InputStream
nie jest interfejsemCo powiesz na:
źródło
Aby podzielić na
InputStream
dwie części, unikając ładowania wszystkich danych do pamięci , a następnie przetwarzania ich niezależnie:OutputStream
dokładnie kilka:PipedOutputStream
PipedInputStream
są zwracaneInputStream
.OutputStream
. Więc wszystko, co przeczytasz ze źródłaInputStream
, będzie napisane w obuOutputStream
. Nie ma takiej potrzeby, ponieważ jest to już zrobione wTeeInputStream
(commons.io).W oddzielnym wątku odczytaj cały źródłowy strumień inputStream, a dane wejściowe są niejawnie przesyłane do docelowych strumieni inputStreams.
Pamiętaj, aby zamknąć inputStreams po wykorzystaniu i zamknąć działający wątek:
TeeInputStream.readAllBytes()
W przypadku, musisz podzielić go na wiele
InputStream
, a nie tylko na dwie. Zastąp w poprzednim fragmencie kodu klasęTeeOutputStream
dla własnej implementacji, która hermetyzuje aList<OutputStream>
i przesłoniOutputStream
interfejs:źródło
Konwertuj strumień wejściowy na bajty, a następnie przekaż go do funkcji savefile, w której składasz to samo do strumienia wejściowego. Również w oryginalnej funkcji używaj bajtów do innych zadań
źródło
W przypadku, gdy ktoś korzysta z aplikacji Spring Boot i chcesz przeczytać treść odpowiedzi
RestTemplate
(dlatego chcę dwukrotnie przeczytać strumień), istnieje czysty sposób na zrobienie tego.Przede wszystkim musisz użyć Springa,
StreamUtils
aby skopiować strumień do String:Ale to nie wszystko. Musisz także użyć fabryki żądań, która może buforować strumień, na przykład:
Lub, jeśli używasz fasoli fabrycznej, to (to jest Kotlin, ale mimo wszystko):
Źródło: https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/
źródło
Jeśli używasz RestTemplate do wykonywania połączeń http, po prostu dodaj przechwytywacz. Treść odpowiedzi jest buforowana przez implementację ClientHttpResponse. Teraz strumień wejściowy może być pobierany z odpowiedzi tyle razy, ile potrzebujemy
źródło