J'ai un InputStream que je passe à une méthode pour faire du traitement. J'utiliserai le même InputStream dans une autre méthode, mais après le premier traitement, l'InputStream semble être fermé à l'intérieur de la méthode.
Comment puis-je cloner le InputStream pour l'envoyer à la méthode qui le ferme? Il y a une autre solution?
EDIT: la méthode qui ferme l'InputStream est une méthode externe à partir d'une bibliothèque. Je n'ai pas de contrôle sur la fermeture ou non.
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
la source
la source
Réponses:
Si tout ce que vous voulez faire est de lire les mêmes informations plus d'une fois et que les données d'entrée sont suffisamment petites pour tenir en mémoire, vous pouvez copier les données de votre
InputStream
vers un ByteArrayOutputStream .Ensuite, vous pouvez obtenir le tableau d'octets associé et ouvrir autant de ByteArrayInputStream "clonés" que vous le souhaitez.
Mais si vous avez vraiment besoin de garder le flux d'origine ouvert pour recevoir de nouvelles données, vous devrez suivre cette
close()
méthode externe et l'empêcher d'être appelée d'une manière ou d'une autre.MISE À JOUR (2019):
Depuis Java 9, les bits du milieu peuvent être remplacés par
InputStream.transferTo
:la source
TeeInputStream
comme décrit dans la réponse ici .Vous souhaitez utiliser Apache
CloseShieldInputStream
:Il s'agit d'un wrapper qui empêchera la fermeture du flux. Vous feriez quelque chose comme ça.
la source
CloseShield
ne fonctionne pas car votreHttpURLConnection
flux d'entrée d' origine est fermé quelque part. Votre méthode ne devrait-elle pas appeler IOUtils avec le flux protégéIOUtils.toString(csContent,charset)
?close()
appel, mais le fait que le Stream est lu jusqu'à la fin. Puisquemark()
etreset()
ne sont peut-être pas les meilleures méthodes pour les connexions http, vous devriez peut-être jeter un coup d'œil à l'approche du tableau d'octets décrite dans ma réponse.Vous ne pouvez pas le cloner et la manière dont vous allez résoudre votre problème dépend de la source des données.
Une solution consiste à lire toutes les données de InputStream dans un tableau d'octets, puis à créer un ByteArrayInputStream autour de ce tableau d'octets et à transmettre ce flux d'entrée à votre méthode.
Edit 1: Autrement dit, si l'autre méthode doit également lire les mêmes données. Ie vous voulez "réinitialiser" le flux.
la source
Si les données lues à partir du flux sont volumineuses, je recommanderais d'utiliser un TeeInputStream d'Apache Commons IO. De cette façon, vous pouvez essentiellement répliquer l'entrée et passer un tube t'd comme votre clone.
la source
Cela peut ne pas fonctionner dans toutes les situations, mais voici ce que j'ai fait: j'ai étendu la classe FilterInputStream et effectué le traitement requis des octets lorsque la bibliothèque externe lit les données.
Ensuite, vous passez simplement une instance de l'
StreamBytesWithExtraProcessingInputStream
endroit où vous auriez passé dans le flux d'entrée. Avec le flux d'entrée d'origine comme paramètre de constructeur.Il convient de noter que cela fonctionne octet pour octet, donc ne l'utilisez pas si des performances élevées sont requises.
la source
UPD. Vérifiez le commentaire avant. Ce n'est pas exactement ce qui a été demandé.
Si vous utilisez,
apache.commons
vous pouvez copier des flux en utilisantIOUtils
.Vous pouvez utiliser le code suivant:
Voici l'exemple complet adapté à votre situation:
Ce code nécessite certaines dépendances:
MAVEN
GRADLE
Voici la référence DOC pour cette méthode:
Vous pouvez en savoir plus
IOUtils
ici: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)la source
Voici la solution avec Kotlin.
Vous pouvez copier votre InputStream dans ByteArray
Si vous avez besoin de lire
byteInputStream
plusieurs fois, appelezbyteInputStream.reset()
avant de relire.https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/
la source
La classe ci-dessous devrait faire l'affaire. Créez simplement une instance, appelez la méthode "multiplier" et fournissez le flux d'entrée source et la quantité de doublons dont vous avez besoin.
Important: vous devez consommer tous les flux clonés simultanément dans des threads séparés.
la source
Le clonage d'un flux d'entrée peut ne pas être une bonne idée, car cela nécessite une connaissance approfondie des détails du flux d'entrée en cours de clonage. Une solution de contournement pour cela consiste à créer un nouveau flux d'entrée qui lit à nouveau à partir de la même source.
Donc, en utilisant certaines fonctionnalités de Java 8, cela ressemblerait à ceci:
Cette méthode a pour effet positif de réutiliser le code déjà en place - la création du flux d'entrée encapsulé dans
inputStreamSupplier
. Et il n'est pas nécessaire de maintenir un deuxième chemin de code pour le clonage du flux.D'un autre côté, si la lecture à partir du flux coûte cher (parce que c'est fait sur une connexion à faible bande passante), alors cette méthode doublera les coûts. Cela pourrait être contourné en utilisant un fournisseur spécifique qui stockera d'abord le contenu du flux localement et fournira une
InputStream
ressource pour cette ressource maintenant locale.la source
is
?java.io.File
oujava.net.URL
par exemple pour créer un nouveau flux d'entrée à chaque fois qu'il est appelé.