Подтвердить что ты не робот

Как связать несколько разных InputStreams в один InputStream

Мне интересно, есть ли какой-нибудь идеоматический способ связать несколько InputStreams с одним непрерывным InputStream в Java (или Scala).

Для этого мне нужно разобрать плоские файлы, которые я загружаю через сеть с FTP-сервера. Я хочу сделать файл [1..N], открыть потоки, а затем объединить их в один поток. Поэтому, когда файл1 подходит к концу, я хочу начать чтение из файла2 и так далее, пока не дойду до конца файлаN.

Мне нужно прочитать эти файлы в определенном порядке, данные поступают из унаследованной системы, которая создает файлы в барах, поэтому данные в одном зависят от данных в другом файле, но я хотел бы обрабатывать их как один непрерывный поток, чтобы упростить мои логический интерфейс домена.

Я обыскал и нашел PipedInputStream, но я не уверен, что это то, что мне нужно. Пример будет полезен.

4b9b3361

Ответ 1

Это прямо в JDK! Цитирование JavaDoc SequenceInputStream:

A SequenceInputStream представляет логическую конкатенацию других входных потоков. Он начинается с упорядоченного набора входных потоков и считывается с первого до тех пор, пока не будет достигнут конец файла, после чего он будет считываться со второго и так далее, пока не будет достигнут конец файла в последнем из содержащихся входных потоков.

Вы хотите объединить произвольное число InputStream, а SequenceInputStream принимает только два. Но так как SequenceInputStream также является InputStream, вы можете применить его рекурсивно (вложить их):

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

... вы поняли идею.

См. также

Ответ 2

Это делается с использованием SequencedInputStream, что прямо на Java, как показывает ответ Томаша Нуркевича. Мне пришлось сделать это несколько раз в проекте недавно, поэтому я добавил немного Scala -y доброту через шаблон "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)
  }
}

С этим я могу выполнить последовательность последовательности следующим образом

val mergedStream = stream1++stream2++stream3

или даже

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

Ответ 3

Другое решение: сначала создайте список входного потока, а затем создайте последовательность входных потоков:

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

Ответ 4

Вот более элегантное решение с использованием Vector, это специально для Android, но используйте вектор для любой 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