wake-up-neo.com

So verketten Sie mehrere verschiedene InputStreams in einem InputStream

Ich frage mich, ob es eine ideomatische Möglichkeit gibt, mehrere InputStreams in einem kontinuierlichen InputStream in Java (oder Scala) zu verketten.

Dafür brauche ich flache Dateien, die ich über das Netzwerk von einem FTP-Server aus lade. Ich möchte die Datei [1..N] nehmen, Streams öffnen und diese dann zu einem Stream zusammenfassen. Wenn file1 dann zu Ende geht, möchte ich aus file2 und so weiter lesen, bis ich das Ende von fileN erreiche. 

Ich muss diese Dateien in einer bestimmten Reihenfolge lesen. Die Daten stammen aus einem Altsystem, das Dateien in Flanken erzeugt. Die Daten in einer Datei hängen von den Daten in einer anderen Datei ab. Ich möchte sie jedoch als einen kontinuierlichen Stream behandeln, um meine Domänenlogikschnittstelle zu vereinfachen . 

Ich habe herumgesucht und PipedInputStream gefunden, aber ich bin nicht sicher, dass es das ist, was ich brauche. Ein Beispiel wäre hilfreich.

54
Magnus

Es ist genau dort in JDK! Zitieren JavaDoc von SequenceInputStream :

Eine SequenceInputStream repräsentiert die logische Verkettung anderer Eingabeströme. Es beginnt mit einer geordneten Sammlung von Eingabeströmen und Lesevorgängen vom ersten bis zum Ende der Datei, woraufhin es vom zweiten liest, usw., bis das Ende der Datei auf dem letzten der enthaltenen Eingabeströme erreicht ist.

Sie möchten eine beliebige Anzahl von InputStreams verketten, während SequenceInputStream nur zwei akzeptiert. Da SequenceInputStream auch eine InputStream ist, können Sie sie rekursiv anwenden (verschachteln):

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

...Du hast die Idee.

Siehe auch

91

Dies geschieht mit SequencedInputStream, was in Java unkompliziert ist, wie die Antwort von Tomasz Nurkiewicz zeigt. Ich musste dies kürzlich wiederholt in einem Projekt durchführen, also habe ich Scala-y-Güte über das Muster "pimp my library" hinzugefügt.

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)
  }
}

Damit kann ich die Stream-Sequenzierung wie folgt durchführen

val mergedStream = stream1++stream2++stream3

oder auch

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

Eine andere Lösung: Erstellen Sie zuerst eine Liste der Eingabeströme und dann die Reihenfolge der Eingabeströme:

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

Hier ist eine elegantere Lösung unter Verwendung von Vector, dies ist für Android spezifisch, aber Vector für jedes 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

Hier ist eine einfache Scala-Version, die einen Iterator[InputStream] verkettet:

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

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