Ich habe einen InputStream, den ich an eine Methode übergebe, um etwas zu verarbeiten. Ich werde den gleichen InputStream in einer anderen Methode verwenden, aber nach der ersten Verarbeitung scheint der InputStream innerhalb der Methode geschlossen zu sein.
Wie kann ich den InputStream klonen, um ihn an die Methode zu senden, die ihn schließt? Gibt es eine andere Lösung?
BEARBEITEN: Die Methoden, die den InputStream schließen, sind eine externe Methode aus einer Bibliothek. Ich habe keine Kontrolle über das Schließen oder nicht.
private String getContent(HttpURLConnection con) {
InputStream content = null;
String charset = "";
try {
content = con.getInputStream();
CloseShieldInputStream csContent = new CloseShieldInputStream(content);
charset = getCharset(csContent);
return IOUtils.toString(content,charset);
} catch (Exception e) {
System.out.println("Error downloading page: " + e);
return null;
}
}
private String getCharset(InputStream content) {
try {
Source parser = new Source(content);
return parser.getEncoding();
} catch (Exception e) {
System.out.println("Error determining charset: " + e);
return "UTF-8";
}
}
java
clone
inputstream
Renato Dinhani
quelle
quelle
Antworten:
Wenn Sie nur dieselben Informationen mehrmals lesen möchten und die Eingabedaten klein genug sind, um in den Speicher zu passen, können Sie die Daten von Ihrem
InputStream
in einen ByteArrayOutputStream kopieren .Dann können Sie das zugehörige Array von Bytes erhalten und so viele "geklonte" ByteArrayInputStreams öffnen, wie Sie möchten .
Wenn Sie jedoch den ursprünglichen Stream wirklich offen halten müssen, um neue Daten zu erhalten, müssen Sie diese externe
close()
Methode verfolgen und verhindern, dass sie irgendwie aufgerufen wird.UPDATE (2019):
Seit Java 9 können die mittleren Bits ersetzt werden durch
InputStream.transferTo
:quelle
TeeInputStream
wie in der Antwort hier beschrieben verwenden .Sie möchten Apaches verwenden
CloseShieldInputStream
:Dies ist ein Wrapper, der verhindert, dass der Stream geschlossen wird. Du würdest so etwas tun.
quelle
CloseShield
funktioniert nicht, weil Ihr ursprünglicherHttpURLConnection
Eingabestream irgendwo geschlossen wird. Sollte Ihre Methode nicht IOUtils mit dem geschützten Stream aufrufenIOUtils.toString(csContent,charset)
?close()
Anruf, sondern die Tatsache, dass der Stream bis zum Ende gelesen wird. Damark()
undreset()
nicht die besten Methoden für HTTP - Verbindungen sein kann, sollten Sie vielleicht einen Blick auf dem Byte - Array - Ansatz auf meiner Antwort beschrieben.Sie können es nicht klonen, und wie Sie Ihr Problem lösen, hängt von der Datenquelle ab.
Eine Lösung besteht darin, alle Daten aus dem InputStream in ein Byte-Array zu lesen, dann einen ByteArrayInputStream um dieses Byte-Array herum zu erstellen und diesen Eingabestream an Ihre Methode zu übergeben.
Bearbeiten 1: Das heißt, wenn die andere Methode ebenfalls dieselben Daten lesen muss. Dh Sie möchten den Stream "zurücksetzen".
quelle
Wenn die aus dem Stream gelesenen Daten groß sind, würde ich die Verwendung eines TeeInputStream von Apache Commons IO empfehlen. Auf diese Weise können Sie die Eingabe im Wesentlichen replizieren und eine t'd-Pipe als Klon übergeben.
quelle
Dies funktioniert möglicherweise nicht in allen Situationen, aber ich habe Folgendes getan: Ich habe die FilterInputStream- Klasse erweitert und die erforderliche Verarbeitung der Bytes durchgeführt, während die externe Bibliothek die Daten liest.
Dann übergeben Sie einfach eine Instanz, an der
StreamBytesWithExtraProcessingInputStream
Sie im Eingabestream übergeben hätten. Mit dem ursprünglichen Eingabestream als Konstruktorparameter.Es sollte beachtet werden, dass dies Byte für Byte funktioniert. Verwenden Sie dies also nicht, wenn eine hohe Leistung erforderlich ist.
quelle
UPD. Überprüfen Sie den Kommentar vor. Es ist nicht genau das, was gefragt wurde.
Wenn Sie verwenden
apache.commons
, können Sie Streams mit kopierenIOUtils
.Sie können folgenden Code verwenden:
Hier ist das vollständige Beispiel, das für Ihre Situation geeignet ist:
Dieser Code erfordert einige Abhängigkeiten:
MAVEN
GRADLE
Hier ist die DOC-Referenz für diese Methode:
Weitere Informationen finden Sie
IOUtils
hier: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)quelle
Unten ist die Lösung mit Kotlin.
Sie können Ihren InputStream in ByteArray kopieren
Wenn Sie das
byteInputStream
mehrmals lesen müssen , rufen Sie an,byteInputStream.reset()
bevor Sie es erneut lesen.https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/
quelle
Die Klasse unten sollte den Trick machen. Erstellen Sie einfach eine Instanz, rufen Sie die "Multiplikations" -Methode auf und geben Sie den Quelleneingabestream und die Anzahl der benötigten Duplikate an.
Wichtig: Sie müssen alle geklonten Streams gleichzeitig in separaten Threads verwenden.
quelle
Das Klonen eines Eingabestreams ist möglicherweise keine gute Idee, da hierfür fundierte Kenntnisse über die Details des zu klonenden Eingabestreams erforderlich sind. Eine Problemumgehung besteht darin, einen neuen Eingabestream zu erstellen, der erneut aus derselben Quelle liest.
Wenn Sie also einige Java 8-Funktionen verwenden, sieht dies folgendermaßen aus:
Diese Methode hat den positiven Effekt, dass bereits vorhandener Code wiederverwendet wird - die Erstellung des eingekapselten Eingabestreams
inputStreamSupplier
. Für das Klonen des Streams muss kein zweiter Codepfad verwaltet werden.Wenn andererseits das Lesen aus dem Stream teuer ist (weil es über eine Verbindung mit geringer Bandbreite erfolgt), verdoppelt diese Methode die Kosten. Dies könnte umgangen werden, indem ein bestimmter Anbieter verwendet wird, der den Stream-Inhalt zuerst lokal speichert und eine
InputStream
für diese jetzt lokale Ressource bereitstellt .quelle
is
?java.io.File
oder verwendenjava.net.URL
, um bei jedem Aufruf einen neuen Eingabestream zu erstellen.