【问题标题】:How to chain multiple different InputStreams into one InputStream如何将多个不同的 InputStream 链接到一个 InputStream
【发布时间】:2012-12-27 00:50:13
【问题描述】:

我想知道是否有任何理想的方式可以将多个 InputStream 链接到 Java(或 Scala)中的一个连续 InputStream 中。

我需要它来解析我从 FTP 服务器通过网络加载的平面文件。我想要做的是获取文件[1..N],打开流,然后将它们组合成一个流。因此,当 file1 结束时,我想从 file2 开始读取,依此类推,直到到达 fileN 的末尾。

我需要以特定顺序读取这些文件,数据来自一个遗留系统,该系统在 barches 中生成文件,因此一个文件中的数据依赖于另一个文件中的数据,但我想将它们作为一个连续流处理以简化我的域逻辑接口。

我四处搜索并找到了 PipedInputStream,但我并不肯定这就是我所需要的。举个例子会很有帮助。

【问题讨论】:

    标签: java scala io inputstream


    【解决方案1】:

    它就在 JDK 中!引用JavaDoc of SequenceInputStream:

    SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,从第一个读取直到到达文件末尾,然后从第二个读取,依此类推,直到最后一个包含的输入流到达文件末尾。

    您想连接任意数量的InputStreams,而SequenceInputStream 只接受两个。但由于 SequenceInputStream 也是 InputStream 您可以递归应用它(嵌套它们):

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

    ...你明白了。

    另见

    【讨论】:

    • 噢!今天在谷歌搜索时,我的关键字一定很糟糕。因为我给它一个很好的谷歌搜索。我想我不能成为第一个需要这个的人,谢谢!
    • SequenceInputStream 构造函数也接受枚举,因此您不必进行嵌套。
    • 如果我使用这种技术链接数百个输入流,他们会担心吗?
    • @pretzels1337,有点晚了。是的,有一个问题:在创建输入流的枚举时,您将一次打开所有文件,并且可以同时打开的文件数量有限制(由操作系统设置)。对于这个用例,我宁愿复制 SequenceInputStream 代码,将Enumeration<InputStream> 替换为List<File>,并相应地更改nextStream()close() 方法。你可以提供你自己的Enumeration<InputStream> 来打开nextElement() 中的流,但是看看close() 在第一个输入流上发生IOExcpetion 的情况下会发生什么
    • 我可以在 SequenceInputStream 中寻找随机位置吗?我的意思是向前和向后。
    【解决方案2】:

    这是使用 SequencedInputStream 完成的,这在 Java 中很简单,正如 Tomasz Nurkiewicz 的回答所示。最近我不得不在一个项目中反复这样做,所以我通过“pimp my library”模式添加了一些 Scala 的优点。

    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 都使用 vector

            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
        

        【讨论】:

          【解决方案5】:

          这是一个连接 Iterator[InputStream] 的简单 Scala 版本:

          import java.io.{InputStream, SequenceInputStream}
          import scala.collection.JavaConverters._
          
          def concatInputStreams(streams: Iterator[InputStream]): InputStream =
            new SequenceInputStream(streams.asJavaEnumeration)
          

          【讨论】:

            猜你喜欢
            • 2016-12-19
            • 2019-01-05
            • 1970-01-01
            • 1970-01-01
            • 2018-08-22
            • 2021-07-17
            • 2015-08-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多