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.
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 InputStream
s 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.
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(_++_)
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)))
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
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)