Comment lisez-vous deux fois le même flux d'entrée? Est-il possible de le copier d'une manière ou d'une autre?
J'ai besoin d'obtenir une image sur le Web, de l'enregistrer localement, puis de renvoyer l'image enregistrée. J'ai juste pensé qu'il serait plus rapide d'utiliser le même flux au lieu de démarrer un nouveau flux vers le contenu téléchargé, puis de le relire.
java
inputstream
Warpzit
la source
la source
Réponses:
Vous pouvez utiliser
org.apache.commons.io.IOUtils.copy
pour copier le contenu de InputStream dans un tableau d'octets, puis lire à plusieurs reprises à partir du tableau d'octets à l'aide d'un ByteArrayInputStream. Par exemple:la source
Selon la provenance de l'InputStream, vous ne pourrez peut-être pas le réinitialiser. Vous pouvez vérifier si
mark()
etreset()
sont pris en charge en utilisantmarkSupported()
.Si c'est le cas, vous pouvez appeler
reset()
InputStream pour revenir au début. Sinon, vous devez à nouveau lire InputStream à partir de la source.la source
InputStream
classes commeBufferedInputStream
supporte 'mark'si votre
InputStream
support en utilisant la marque, vous pouvez alorsmark()
votre inputStream et ensuitereset()
. si votreInputStrem
ne prend pas en charge la marque, vous pouvez utiliser la classejava.io.BufferedInputStream
, vous pouvez donc intégrer votre flux dans unBufferedInputStream
comme celui-cila source
BufferedInputStream.fill()
, il y a la section "Grow buffer", où la nouvelle taille de tampon est comparée uniquement àmarklimit
etMAX_BUFFER_SIZE
.Vous pouvez encapsuler le flux d'entrée avec PushbackInputStream. PushbackInputStream permet de non lu ( « écriture différée ») octets qui ont été déjà lu, de sorte que vous pouvez faire comme ceci:
Veuillez noter que PushbackInputStream stocke le tampon interne d'octets, donc il crée vraiment un tampon en mémoire qui contient les octets "réécrits".
Connaissant cette approche, nous pouvons aller plus loin et la combiner avec FilterInputStream. FilterInputStream stocke le flux d'entrée d'origine en tant que délégué. Cela permet de créer une nouvelle définition de classe qui permet de " non lire " automatiquement les données originales. La définition de cette classe est la suivante:
Cette classe a deux méthodes. Un pour lire dans le tampon existant (la définition est analogue à l'appel
public int read(byte b[], int off, int len)
de la classe InputStream). Deuxième qui renvoie un nouveau tampon (cela peut être plus efficace si la taille du tampon à lire est inconnue).Voyons maintenant notre classe en action:
la source
Si vous utilisez une implémentation de
InputStream
, vous pouvez vérifier le résultat deInputStream#markSupported()
cela pour vous dire si vous pouvez ou non utiliser la méthodemark()
/reset()
.Si vous pouvez marquer le flux lorsque vous lisez, appelez
reset()
pour revenir en arrière pour commencer.Si vous ne pouvez pas, vous devrez ouvrir à nouveau un flux.
Une autre solution serait de convertir InputStream en tableau d'octets, puis d'itérer sur le tableau autant de fois que nécessaire. Vous pouvez trouver plusieurs solutions dans cet article Convertir InputStream en tableau d'octets en Java utilisant ou non des tierces. Attention, si le contenu lu est trop volumineux, vous risquez de rencontrer des problèmes de mémoire.
Enfin, si vous avez besoin de lire l'image, utilisez:
L'utilisation
ImageIO#read(java.net.URL)
vous permet également d'utiliser le cache.la source
ImageIO#read(java.net.URL)
: certains serveurs Web et CDN peuvent rejeter les appels simples (c'est-à-dire sans agent utilisateur qui fait croire au serveur que l'appel provient d'un navigateur Web) effectués parImageIO#read
. Dans ce cas, l'utilisation de laURLConnection.openConnection()
configuration de l'agent utilisateur sur cette connexion + l'utilisation de `ImageIO.read (InputStream) fera, la plupart du temps, l'affaire.InputStream
n'est pas une interfaceQue diriez-vous:
la source
Pour fractionner un
InputStream
en deux, en évitant de charger toutes les données en mémoire , puis de les traiter indépendamment:OutputStream
, précisément:PipedOutputStream
PipedInputStream
sont les fichiers renvoyésInputStream
.OutputStream
. Donc, tout le lire à partir du sourcingInputStream
serait écrit dans les deuxOutputStream
. Il n'est pas nécessaire de l'implémenter, car cela est déjà fait dansTeeInputStream
(commons.io).Dans un thread séparé, lisez l'intégralité de la source inputStream et implicitement, les données d'entrée sont transférées vers les inputStreams cibles.
Soyez conscient de fermer les inputStreams après avoir été consommés et fermez le thread qui s'exécute:
TeeInputStream.readAllBytes()
Au cas où vous auriez besoin de le diviser en plusieurs
InputStream
, au lieu de seulement deux. Remplacez dans le fragment de code précédent la classeTeeOutputStream
de votre propre implémentation, qui encapsulerait aList<OutputStream>
et remplacerait l'OutputStream
interface:la source
Convertissez le flux d'entrée en octets, puis passez-le à la fonction savefile où vous assemblez le même flux d'entrée. Également dans la fonction d'origine, utilisez des octets à utiliser pour d'autres tâches
la source
Dans le cas où quelqu'un s'exécute dans une application Spring Boot et que vous souhaitez lire le corps de la réponse d'un
RestTemplate
(c'est pourquoi je veux lire un flux deux fois), il existe un moyen propre (plus) de le faire.Tout d'abord, vous devez utiliser Spring
StreamUtils
pour copier le flux dans une chaîne:Mais ce n'est pas tout. Vous devez également utiliser une fabrique de requêtes qui peut mettre en mémoire tampon le flux pour vous, comme ceci:
Ou, si vous utilisez le bean d'usine, alors (c'est Kotlin mais quand même):
Source: https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/
la source
Si vous utilisez RestTemplate pour passer des appels http, ajoutez simplement un intercepteur. Le corps de la réponse est mis en cache par l'implémentation de ClientHttpResponse. Le flux d'entrée peut maintenant être récupéré à partir de respose autant de fois que nécessaire
la source