java InputStream 可以从方法中连续读取数据吗?

Can a java InputStream continuously read data from a method?

我有一段代码

...
InputStream inputStream = new BufferedInputStream(new ByteArrayInputStream("test".getBytes()));
...

并且此行使字符串“test”成为 InputStream 的输入,但这是一个静态 InputStream。 没有扫描仪,System.in 或用户外部输入有没有办法使这个 InputStream 动态

我需要的是这样的东西

...
InputStream inputStream = new BufferedInputStream(new 
ByteArrayInputStream(generateContinuousDynamicString().getBytes()));
// So, basically input stream will be blocked until generateContinuousDynamicString()
// returns a result?
...

我试过这样的东西

private static byte[] generateContinuousDynamicString(String s) {
    String t = "";
    // here comes the realization
    // that the source for an input stream 
    // cannot be generated dynamically on the 
    // fly it only can be read from already 
    // existing (fully generated and available 
    // resource). Am I right? Otherwise how 
    // can I adjust this method in such a way that
    // input stream would continuously have a new
    // string to read from? 
    for (int i = 0; i < 1000; i++){
        t += "<str>"+s+i+"</str>";
    }
    return ("<test>"+t+"</test>").getBytes();
}

所以,如果我们有

...
InputStream inputStream = new BufferedInputStream(readFromADatabaseStream());
...

这也不是动态输入流,因为资源已经在数据库中。

当然可以。但是你有一个问题:无论什么代码正在生成无穷无尽的动态数据流,都不能仅在 'returns the inputstream' 本身的方法中,这就是你的实现。

您有两个主要选择:

线程

相反,您可以启动一个不断生成数据的线程。请注意,无论什么 'generates' 都需要缓存;这 不太合适 例如,如果您想动态生成一个只提供无限量 0 字节的输入流。如果数据来自,比如说,一个 USB 连接的 arduino,它会不时发送有关它所连接的温度传感器的信息,这是一个很好的选择。请注意,您需要线程将它接收到的数据存储在某个地方,然后有一个输入流将从您正在制作的数据队列中 'pull' 。要创建从队列中提取的输入流,请参阅下一节。由于这将涉及线程,因此使用 java.util.concurrent 中的内容,例如 ArrayBlockingQueue - 这具有双重好处,您也不会获得无限缓冲区(将内容放入缓冲区的行为将阻塞如果缓冲区已满)。

子类化

您还可以将可以生成新值的代码放入一个信封中 - 一个您可以传递的东西。您想编写一些代码,但 而不是 运行 它 - 您想要 运行 稍后,当您将输入流交给它时,调用 .read().

一种简单的方法是 扩展 InputStream - 然后实现您自己的零方法。看起来像这样:


class InfiniteZeroesInputStream extends InputStream {
    public int read() {
        return 0;
    }
}

就这么简单。给定:

try (InputStream in = new InfiniteZeroesInputStream()) {
    in.read(); // returns 0.. and will always do so.
    byte[] b = new byte[65536];
    in.read(b); // fills the whole array with zeroes.
}

你想要一个 管道。 具体来说,你想要以下一对 类:

您的问题要求 InputStream,但由于您处理的是文本,因此您可能应该使用 Reader,它专用于字符。特别要注意的是,对于任何具有 non-ASCII 个字符。使用 Reader 和 Writer 将消除担心这一点的需要。

无论哪种方式,方法都是相同的:创建管道的可读端,然后在另一个线程中创建并提供管道的可写端。

使用 PipedReader 和 PipedWriter:

PipedReader pipedReader = new PipedReader();
Reader reader = new BufferedReader(pipedReader);

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> pipeFeeder = executor.submit(
    () -> generateContinuousDynamicString(pipedReader));

// ...

private Void generateContinuousDynamicString(PipedReader pipedReader)
throws IOException {

    try (Writer writer = new PipedWriter(pipedReader)) {
        writer.write("<test>");

        for (int i = 0; i < 1000; i++) {
            writer.write("<str>" + i + "</str>");
        }

        writer.write("</test>");
    }

    return null;
}

使用 PipedInputStream 和 PipedOutputStream:

PipedInputStream pipedInputStream = new PipedInputStream();
InputStream inputStream = new BufferedInputStream(pipedInputStream);

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> pipeFeeder = executor.submit(
    () -> generateContinuousDynamicString(pipedInputStream));

// ...

private Void generateContinuousDynamicString(PipedInputStream pipedInputStream)
throws IOException {

    Charset charset = StandardCharsets.UTF_8;

    try (Writer writer = new OutputStreamWriter(
            new PipedInputStream(pipedinputStream),
            StandardCharsets.UTF_8)) {

        writer.write("<test>");

        for (int i = 0; i < 1000; i++) {
            writer.write("<str>" + i + "</str>");
        }

        writer.write("</test>");
    }

    return null;
}