web-dev-qa-db-fra.com

Comment chaîner plusieurs InputStreams différents en un seul InputStream

Je me demande s’il existe un moyen idéal de chaîner plusieurs InputStreams en un seul InputStream continu en Java (ou Scala).

Ce dont j'ai besoin, c'est d'analyser les fichiers plats que je charge sur le réseau à partir d'un serveur FTP. Ce que je veux faire, c'est prendre le fichier [1..N], ouvrir des flux, puis les combiner en un seul. Ainsi, lorsque le fichier 1 se termine, je souhaite commencer à lire le fichier 2, et ainsi de suite, jusqu'à la fin du fichier N. 

J'ai besoin de lire ces fichiers dans un ordre spécifique, les données proviennent d'un système hérité qui produit des fichiers en barches de sorte que les données contenues dans un fichier dépendent des données d'un autre fichier, mais j'aimerais les traiter comme un flux continu pour simplifier l'interface logique de mon domaine. . 

J'ai cherché et trouvé PipedInputStream, mais je ne suis pas certain que ce soit ce dont j'ai besoin. Un exemple serait utile.

54
Magnus

C'est juste là dans JDK! Citations JavaDoc of SequenceInputStream :

Un SequenceInputStream représente la concaténation logique des autres flux d'entrée. Il commence par une collection ordonnée de flux d'entrée et lit à partir du premier jusqu'à la fin du fichier, après quoi il lit à partir du second, et ainsi de suite, jusqu'à la fin du fichier sur le dernier des flux d'entrée contenus.

Vous voulez concaténer un nombre arbitraire de InputStreams alors que SequenceInputStream n'en accepte que deux. Mais puisque SequenceInputStream est aussi une InputStream, vous pouvez l'appliquer de manière récursive (imbriquer):

new SequenceInputStream(
    new SequenceInputStream(
        new SequenceInputStream(file1, file2),
        file3
    ),
    file4
);

...vous avez eu l'idée.

Voir également

91
Tomasz Nurkiewicz

Ceci est fait en utilisant SequencedInputStream, ce qui est simple en Java, comme le montre la réponse de Tomasz Nurkiewicz. J'ai eu à faire cela à plusieurs reprises récemment dans un projet, alors j'ai ajouté un peu de bonté Scala-y via le motif "pimp my library".

object StreamUtils {
  implicit def toRichInputStream(str: InputStream) = new RichInputStream(str)

  class RichInputStream(str: InputStream) {
// a bunch of other handy Stream functionality, deleted

    def ++(str2: InputStream): InputStream = new SequenceInputStream(str, str2)
  }
}

Avec cela, je peux faire le séquençage de flux comme suit

val mergedStream = stream1++stream2++stream3

ou même

val streamList = //some arbitrary-length list of streams, non-empty
val mergedStream = streamList.reduceLeft(_++_)
13
Dave Griffith

Une autre solution: commencez par créer une liste de flux d’entrée, puis créez la séquence de flux d’entrée:

List<InputStream> iss = Files.list(Paths.get("/your/path"))
        .filter(Files::isRegularFile)
        .map(f -> {
            try {
                return new FileInputStream(f.toString());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList());

new SequenceInputStream(Collections.enumeration(iss)))
9
freedev

Voici une solution plus élégante en utilisant Vector, ceci est spécialement pour Android, mais utilisez vector pour tout Java

    AssetManager am = getAssets();
    Vector v = new Vector(Constant.PAGES);
    for (int i =  0; i < Constant.PAGES; i++) {
        String fileName = "file" + i + ".txt";
         InputStream is = am.open(fileName);
         v.add(is);
    }
    Enumeration e = v.elements();
    SequenceInputStream sis = new SequenceInputStream(e);
    InputStreamReader isr = new InputStreamReader(sis);

    Scanner scanner = new Scanner(isr);   // or use bufferedReader
1
Ryan Heitner

Voici une version simple de Scala qui concatène un Iterator[InputStream]:

import Java.io.{InputStream, SequenceInputStream}
import scala.collection.JavaConverters._

def concatInputStreams(streams: Iterator[InputStream]): InputStream =
  new SequenceInputStream(streams.asJavaEnumeration)
0
Carl Ekerot