Wie liest du denselben Inputstream zweimal? Ist es möglich, es irgendwie zu kopieren?
Ich muss ein Bild aus dem Web abrufen, es lokal speichern und dann das gespeicherte Bild zurückgeben. Ich dachte nur, es wäre schneller, denselben Stream zu verwenden, anstatt einen neuen Stream für den heruntergeladenen Inhalt zu starten und ihn dann erneut zu lesen.
java
inputstream
Warpzit
quelle
quelle
Antworten:
Sie können
org.apache.commons.io.IOUtils.copy
den Inhalt des InputStream in ein Byte-Array kopieren und dann mit einem ByteArrayInputStream wiederholt aus dem Byte-Array lesen. Z.B:quelle
Je nachdem, woher der InputStream kommt, können Sie ihn möglicherweise nicht zurücksetzen. Sie können überprüfen, ob
mark()
undreset()
unterstützt werdenmarkSupported()
.Wenn dies
reset()
der Fall ist, können Sie den InputStream aufrufen , um zum Anfang zurückzukehren. Wenn nicht, müssen Sie den InputStream erneut von der Quelle lesen.quelle
InputStream
Unterklassen wieBufferedInputStream
unterstützt "Mark"Wenn Sie
InputStream
Unterstützung mit Mark unterstützen, können Siemark()
Ihren inputStream und dannreset()
es. wenn IhrInputStrem
nicht Zeichen nicht unterstützt , dann können Sie die Klasse verwendenjava.io.BufferedInputStream
, so können Sie einbetten Stream innerhalb einesBufferedInputStream
wie diesesquelle
BufferedInputStream.fill()
, es gibt den Abschnitt "Wachstumspuffer", in dem die neue Puffergröße nur mitmarklimit
und verglichen wirdMAX_BUFFER_SIZE
.Sie können den Eingabestream mit PushbackInputStream umbrechen. Mit PushbackInputStream können bereits gelesene Bytes ungelesen (" zurückgeschrieben ") werden. So können Sie Folgendes tun:
Bitte beachten Sie, dass PushbackInputStream den internen Byte-Puffer speichert, sodass tatsächlich ein Puffer im Speicher erstellt wird, der "zurückgeschriebene" Bytes enthält.
Wenn wir diesen Ansatz kennen, können wir noch weiter gehen und ihn mit FilterInputStream kombinieren. FilterInputStream speichert den ursprünglichen Eingabestream als Delegat. Auf diese Weise kann eine neue Klassendefinition erstellt werden, mit der Originaldaten automatisch " ungelesen " werden können. Die Definition dieser Klasse lautet wie folgt:
Diese Klasse hat zwei Methoden. Eine zum Einlesen in vorhandenen Puffer (Definition ist analog zum Aufrufen
public int read(byte b[], int off, int len)
der InputStream-Klasse). Zweitens, der neuen Puffer zurückgibt (dies kann effektiver sein, wenn die Größe des zu lesenden Puffers unbekannt ist).Jetzt sehen wir unsere Klasse in Aktion:
quelle
Wenn Sie eine Implementierung von verwenden
InputStream
, können Sie anhand des Ergebnisses überprüfenInputStream#markSupported()
, ob Sie die Methodemark()
/ verwenden können.reset()
.Wenn Sie den Stream beim Lesen markieren können, rufen Sie
reset()
an, um zurück zu gehen und zu beginnen.Wenn Sie nicht können, müssen Sie einen Stream erneut öffnen.
Eine andere Lösung wäre, InputStream in ein Byte-Array zu konvertieren und dann so oft wie nötig über das Array zu iterieren. In diesem Beitrag finden Sie verschiedene Lösungen. Konvertieren Sie InputStream in ein Byte-Array in Java mit Bibliotheken von Drittanbietern oder nicht. Achtung, wenn der gelesene Inhalt zu groß ist, können Speicherprobleme auftreten.
Wenn Sie das Bild lesen möchten, verwenden Sie:
Mit
ImageIO#read(java.net.URL)
können Sie auch den Cache verwenden.quelle
ImageIO#read(java.net.URL)
: Einige Webserver und CDNs lehnen möglicherweise bloße Anrufe ab (dh ohne einen Benutzeragenten, der den Server glauben lässt, dass der Anruf von einem Webbrowser stammt) vonImageIO#read
. In diesem Fall reicht esURLConnection.openConnection()
meistens aus, den Benutzeragenten mit ImageIO.read (InputStream) auf diese Verbindung + einzustellen.InputStream
ist keine SchnittstelleWie wäre es mit:
quelle
So teilen Sie eine
InputStream
in zwei Teile, ohne zu vermeiden, dass alle Daten in den Speicher geladen und dann unabhängig voneinander verarbeitet werden:OutputStream
, genau:PipedOutputStream
PipedInputStream
sind die zurückgegebenenInputStream
.OutputStream
. Also, alles, was es aus dem Sourcing liestInputStream
, würde in beiden geschrieben seinOutputStream
.TeeInputStream
Dies muss nicht implementiert werden, da dies bereits in (commons.io) erfolgt.Lesen Sie innerhalb eines getrennten Threads den gesamten Sourcing-InputStream, und implizit werden die Eingabedaten an die Ziel-InputStreams übertragen.
Achten Sie darauf, die inputStreams nach dem Konsumieren zu schließen und den ausgeführten Thread zu schließen:
TeeInputStream.readAllBytes()
In diesem Fall müssen Sie es in mehrere
InputStream
statt nur in zwei Teile aufteilen . Ersetzen Sie im vorherigen Codefragment die KlasseTeeOutputStream
für Ihre eigene Implementierung, die a einkapselnList<OutputStream>
und dieOutputStream
Schnittstelle überschreiben würde :quelle
Konvertieren Sie den Eingabestream in Bytes und übergeben Sie ihn dann an die Funktion savefile, wo Sie denselben in den Eingabestream zusammensetzen. Verwenden Sie auch in der ursprünglichen Funktion Bytes, um sie für andere Aufgaben zu verwenden
quelle
Falls jemand in einer Spring Boot-App ausgeführt wird und Sie den Antworttext von a lesen möchten
RestTemplate
(weshalb ich einen Stream zweimal lesen möchte), gibt es eine saubere (er) Möglichkeit, dies zu tun.Zunächst müssen Sie Spring's verwenden,
StreamUtils
um den Stream in einen String zu kopieren:Aber das ist nicht alles. Sie müssen auch eine Anforderungsfactory verwenden, die den Stream für Sie puffern kann, wie folgt:
Oder wenn Sie die Factory Bean verwenden (dann ist dies Kotlin, aber trotzdem):
Quelle: https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/
quelle
Wenn Sie RestTemplate verwenden, um http-Aufrufe zu tätigen, fügen Sie einfach einen Interceptor hinzu. Der Antworttext wird von der Implementierung von ClientHttpResponse zwischengespeichert. Jetzt kann der Inputstream so oft wie nötig aus der Antwort abgerufen werden
quelle