mise à jour en Java9: https://docs.Oracle.com/javase/9/docs/api/Java/io/InputStream.html#transferTo-Java.io.OutputStream-
J'ai vu des discussions similaires, mais pas tout à fait ce qu'il me faut.
J'ai un serveur, qui prend en gros les entrées d'un client, le client A, et les transmet, octet pour octet, à un autre client, le client B.
J'aimerais connecter mon flux d'entrée du client A avec mon flux de sortie du client B. Est-ce possible? Quels sont les moyens de le faire?
En outre, ces clients s'envoient des messages, qui sont quelque peu sensibles au temps, la mise en mémoire tampon ne fonctionnera donc pas. Je ne veux pas d'un tampon de 500, et un client envoie 499 octets. Mon serveur s'abstient de transférer les 500 octets car il n'a pas reçu le dernier octet pour remplir le tampon.
En ce moment, j'analyse chaque message pour trouver sa longueur, puis je lis des octets de longueur, puis je les transfère. J'ai pensé (et testé) que ce serait mieux que de lire un octet et de le transmettre encore et encore car cela serait très lent. Je ne voulais pas non plus utiliser un tampon ou une minuterie pour les raisons que j'ai mentionnées dans mon dernier paragraphe - je ne veux pas que les messages attendent très longtemps, simplement parce que le tampon n'est pas plein.
Quel est un bon moyen de faire cela?
Ce n’est pas parce que vous utilisez un tampon que le flux doit remplir ce tampon. En d'autres termes, ça devrait aller:
public static void copyStream(InputStream input, OutputStream output)
throws IOException
{
byte[] buffer = new byte[1024]; // Adjust if you want
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1)
{
output.write(buffer, 0, bytesRead);
}
}
Cela devrait fonctionner correctement - essentiellement, l'appel read
bloquera jusqu'à ce qu'il y ait certaines données disponibles, mais il n'attendra pas qu'il soit tout disponible pour remplir le tampon . (Je suppose que c'est possible, et je crois que FileInputStream
habituellement va remplir le tampon, mais un flux attaché à un socket est plus susceptible de vous donner les données immédiatement.)
Je pense que cela vaut au moins d'essayer d'abord cette solution simple.
Pourquoi ne pas utiliser
void feedInputToOutput(InputStream in, OutputStream out) {
IOUtils.copy(in, out);
}
et en finir avec ça?
de la bibliothèque i/o de jakarta Apache commons qui est déjà utilisée par un grand nombre de projets, vous avez donc probablement déjà le fichier jar dans votre classpath.
Pour compléter, goyave possède également un tilitaire pratique pour cette
ByteStreams.copy(input, output);
JDK 9 a ajouté InputStream#transferTo(OutputStream out)
pour cette fonctionnalité.
Vous pouvez utiliser un tampon circulaire:
Code
// buffer all data in a circular buffer of infinite size
CircularByteBuffer cbb = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
class1.putDataOnOutputStream(cbb.getOutputStream());
class2.processDataFromInputStream(cbb.getInputStream());
Dépendance Maven
<dependency>
<groupId>org.ostermiller</groupId>
<artifactId>utils</artifactId>
<version>1.07.00</version>
</dependency>
Détails du mode
Manière asynchrone d'y parvenir.
void inputStreamToOutputStream(final InputStream inputStream, final OutputStream out) {
Thread t = new Thread(new Runnable() {
public void run() {
try {
int d;
while ((d = inputStream.read()) != -1) {
out.write(d);
}
} catch (IOException ex) {
//TODO make a callback on exception.
}
}
});
t.setDaemon(true);
t.start();
}
BUFFER_SIZE est la taille des mandrins à lire. Doit être> 1 Ko et <10 Mo.
private static final int BUFFER_SIZE = 2 * 1024 * 1024;
private void copy(InputStream input, OutputStream output) throws IOException {
try {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = input.read(buffer);
while (bytesRead != -1) {
output.write(buffer, 0, bytesRead);
bytesRead = input.read(buffer);
}
//If needed, close streams.
} finally {
input.close();
output.close();
}
}
Utilisez org.Apache.commons.io.IOUtils
InputStream inStream = new ...
OutputStream outStream = new ...
IOUtils.copy(inStream, outStream);
ou copyLarge pour la taille> 2 Go
Ceci est une version Scala propre et rapide (pas de stackoverflow)):
import scala.annotation.tailrec
import Java.io._
implicit class InputStreamOps(in: InputStream) {
def >(out: OutputStream): Unit = pipeTo(out)
def pipeTo(out: OutputStream, bufferSize: Int = 1<<10): Unit = pipeTo(out, Array.ofDim[Byte](bufferSize))
@tailrec final def pipeTo(out: OutputStream, buffer: Array[Byte]): Unit = in.read(buffer) match {
case n if n > 0 =>
out.write(buffer, 0, n)
pipeTo(out, buffer)
case _ =>
in.close()
out.close()
}
}
Cela permet d'utiliser >
symbole, par exemple inputstream > outputstream
et passe également dans les tampons/tailles personnalisés.