Most efficient way to create InputStream from OutputStream
本页:http://blog.ostermiller.org/convert-java-outputstream-inputstream描述如何从OutputStream创建输入流:
1 |
其他的替代方法是使用pipedstream和新的线程,这很麻烦。
我不喜欢将许多兆字节复制到新的内存字节数组中。有没有图书馆能更有效地做到这一点?
编辑:
在劳伦斯·冈萨尔维斯的建议下,我尝试了流水线,结果发现它们并不难处理。以下是Clojure中的示例代码:
1 2 3 4 5 | (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)) |
号
如果您不想同时将所有数据复制到内存缓冲区中,那么您必须让使用输出流(生产者)的代码和使用输入流(使用者)的代码在同一线程中交替使用,或者在两个单独的线程中并发操作。让它们在同一个线程中运行可能比使用两个单独的线程复杂得多,更容易出错(您需要确保使用者从不阻塞等待输入,否则您将有效地死锁),并且需要让生产者和使用者在同一个循环中运行,这似乎是因为它们在同一个循环中紧密地协作。尤普尔
所以使用第二个线程。其实没那么复杂。您链接到的页面有一个完美的示例:
1 2 3 4 5 6 7 8 9 10 | PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(in); new Thread( new Runnable(){ public void run(){ class1.putDataOnOutputStream(out); } } ).start(); class2.processDataFromInputStream(in); |
还有另一个开放源码库easytream,它以透明的方式处理管道和线程。如果一切顺利的话,这并不复杂。出现问题时(看劳伦斯·冈萨尔维斯的例子)
class1.putDataOnOutputStream(out);
号
引发异常。在这个例子中,线程只是完成了,异常丢失了,而外部
EasyStream处理异常传播和我调试了大约一年的其他棘手问题。(我是图书馆的维护者:显然我的解决方案是最好的一个;)下面是一个如何使用它的示例:
1 2 3 4 5 6 7 8 9 10 11 | 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) } }; |
号
还有一个很好的介绍,介绍了将输出流转换为输入流的所有其他方法。值得一看。
避免复制缓冲区的一个简单解决方案是创建一个专用的
1 2 3 4 5 6 7 8 9 10 11 12 | 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); } } |
。
根据需要写入上述输出流,然后调用
我认为将inputstream连接到outputstream的最佳方法是通过管道流-在java.io包中提供,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 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-不需要手动处理数据队列
我通常试图避免创建单独的线程,因为死锁的可能性增加,理解代码的难度增加,以及处理异常的问题。
下面是我建议的解决方案:一个通过重复调用produceChunk()创建分块内容的producerinputstream:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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); } |