91

此页面:http ://blog.ostermiller.org/convert-java-outputstream-inputstream 描述了如何从 OutputStream 创建 InputStream:

new ByteArrayInputStream(out.toByteArray())

其他替代方法是使用 PipedStreams 和繁琐的新线程。

我不喜欢将许多兆字节复制到新的内存字节数组的想法。有没有一个图书馆可以更有效地做到这一点?

编辑:

根据 Laurence Gonsalves 的建议,我尝试了 PipedStreams,结果证明它们并不难处理。这是clojure中的示例代码:

(defn #^PipedInputStream create-pdf-stream [pdf-info]
  (let [in-stream (new PipedInputStream)
        out-stream (PipedOutputStream. in-stream)]
    (.start (Thread. #(;Here you write into out-stream)))
    in-stream))
4

5 回答 5

77

如果您不想一次将所有数据复制到内存缓冲区中,那么您将必须拥有使用 OutputStream (生产者)的代码和使用 InputStream 的代码(消费者) 要么在同一个线程中交替,要么在两个单独的线程中同时操作。让它们在同一个线程中运行可能比使用两个单独的线程复杂得多,更容易出错(你需要确保消费者永远不会阻塞等待输入,否则你会有效地死锁)并且需要让生产者和消费者在同一个循环中运行,这看起来耦合得太紧密了。

所以使用第二个线程。真的没那么复杂。您链接到的页面有合理的例子。这是一个有点现代化的版本,它也关闭了流:

try (PipedInputStream in = new PipedInputStream()) {
    new Thread(() -> {
        try (PipedOutputStream out = new PipedOutputStream(in)) {
            writeDataToOutputStream(out);
        } catch (IOException iox) {
            // handle IOExceptions
        }
    }).start();
    processDataFromInputStream(in);
}
于 2009-08-04T07:06:25.453 回答
14

还有另一个名为EasyStream的开源库,它以透明的方式处理管道和线程。如果一切顺利,这并不复杂。问题出现时(查看 Laurence Gonsalves 示例)

class1.putDataOnOutputStream(out);

引发异常。在该示例中,线程简单地完成并且异常丢失,而外部InputStream可能被截断。

Easystream 处理异常传播和其他我已经调试了大约一年的讨厌问题。(我是图书馆的管理员:显然我的解决方案是最好的;))这是一个如何使用它的例子:

final InputStreamFromOutputStream<String> isos = new InputStreamFromOutputStream<String>(){
 @Override
 public String produce(final OutputStream dataSink) throws Exception {
   /*
    * call your application function who produces the data here
    * WARNING: we're in another thread here, so this method shouldn't 
    * write any class field or make assumptions on the state of the outer class. 
    */
   return produceMydata(dataSink)
 }
};

还有一个很好的介绍,其中解释了将 OutputStream 转换为 InputStream 的所有其他方法。值得一看。

于 2011-06-02T11:30:32.643 回答
11

避免复制缓冲区的一个简单解决方案是创建一个特殊用途ByteArrayOutputStream

public class CopyStream extends ByteArrayOutputStream {
    public CopyStream(int size) { super(size); }

    /**
     * Get an input stream based on the contents of this output stream.
     * Do not use the output stream after calling this method.
     * @return an {@link InputStream}
     */
    public InputStream toInputStream() {
        return new ByteArrayInputStream(this.buf, 0, this.count);
    }
}

根据需要写入上述输出流,然后调用toInputStream以获取底层缓冲区上的输入流。考虑在该点之后关闭输出流。

于 2016-05-01T00:25:20.377 回答
7

我认为将 InputStream 连接到 OutputStream 的最佳方法是通过管道流- 在 java.io 包中可用,如下所示:

// 1- Define stream buffer
private static final int PIPE_BUFFER = 2048;

// 2 -Create PipedInputStream with the buffer
public PipedInputStream inPipe = new PipedInputStream(PIPE_BUFFER);

// 3 -Create PipedOutputStream and bound it to the PipedInputStream object
public PipedOutputStream outPipe = new PipedOutputStream(inPipe);

// 4- PipedOutputStream is an OutputStream, So you can write data to it
// in any way suitable to your data. for example:
while (Condition) {
     outPipe.write(mByte);
}

/*Congratulations:D. Step 4 will write data to the PipedOutputStream
which is bound to the PipedInputStream so after filling the buffer
this data is available in the inPipe Object. Start reading it to
clear the buffer to be filled again by the PipedInputStream object.*/

在我看来,这段代码有两个主要优点:

1 - 除了缓冲区,没有额外的内存消耗。

2 - 您不需要手动处理数据排队

于 2015-01-28T12:11:39.293 回答
2

我通常会尽量避免创建单独的线程,因为这样会增加死锁的机会,增加理解代码的难度,以及处理异常的问题。

这是我提出的解决方案:一个 ProducerInputStream,它通过重复调用 producerChunk() 以块的形式创建内容:

public abstract class ProducerInputStream extends InputStream {

    private ByteArrayInputStream bin = new ByteArrayInputStream(new byte[0]);
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();

    @Override
    public int read() throws IOException {
        int result = bin.read();
        while ((result == -1) && newChunk()) {
            result = bin.read();
        }
        return result;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        int result = bin.read(b, off, len);
        while ((result == -1) && newChunk()) {
            result = bin.read(b, off, len);
        }
        return result;
    }

    private boolean newChunk() {
        bout.reset();
        produceChunk(bout);
        bin = new ByteArrayInputStream(bout.toByteArray());
        return (bout.size() > 0);
    }

    public abstract void produceChunk(OutputStream out);

}
于 2016-11-28T13:19:45.740 回答