Java 从OutputStream创建InputStream的最有效方法

Java 从OutputStream创建InputStream的最有效方法,java,io,stream,inputstream,bytearrayoutputstream,Java,Io,Stream,Inputstream,Bytearrayoutputstream,本页: 描述如何从OutputStream创建InputStream: new ByteArrayInputStream(out.toByteArray()) 其他替代方法是使用管道流和新线程,这很麻烦 我不喜欢将许多兆字节复制到新的内存字节数组的想法。 有没有一个图书馆能更有效地做到这一点 编辑: 根据劳伦斯·贡萨尔维斯(Laurence Gonsalves)的建议,我尝试了管道流,结果发现它们并不难处理。 以下是clojure中的示例代码: (defn #^PipedInputStream

本页: 描述如何从OutputStream创建InputStream:

new ByteArrayInputStream(out.toByteArray())
其他替代方法是使用管道流和新线程,这很麻烦

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

编辑:

根据劳伦斯·贡萨尔维斯(Laurence Gonsalves)的建议,我尝试了管道流,结果发现它们并不难处理。 以下是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))

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

因此,使用第二个线程。其实没那么复杂。您链接到的页面有一个完美的示例:

  PipedInputStream in = new PipedInputStream();
  PipedOutputStream out = new PipedOutputStream(in);
  new Thread(
    new Runnable(){
      public void run(){
        class1.putDataOnOutputStream(out);
      }
    }
  ).start();
  class2.processDataFromInputStream(in);

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

类别1.putDataOnOutputStream(输出)

抛出异常。 在该示例中,线程简单地完成,异常丢失,而外部
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)
 }
};
final InputStreamFromOutputStream isos=新的InputStreamFromOutputStream(){
@凌驾
公共字符串生成(最终输出流数据链路)引发异常{
/*
*在此处调用生成数据的应用程序函数
*警告:我们在另一个线程中,因此此方法不应
*编写任何类字段或对外部类的状态进行假设。
*/
返回produceMydata(数据链接)
}
};

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

我认为将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-您不需要手动处理数据队列

一个避免复制缓冲区的简单解决方案是通过tearrayoutputstream创建一个专用的

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
,以通过底层缓冲区获得输入流。考虑到该点之后关闭的输出流。

< P>我通常试图避免创建一个单独的线程,因为死锁的可能性增加,理解代码的难度增加,处理异常的问题。

下面是我建议的解决方案:一个ProducerInputStream,通过重复调用ProducerChunk()以块的形式创建内容:


我认为您还需要为每个用户线程创建新的PipedInputStream。如果你从另一个线程读取管道,它会给你一个错误。@Lawrence:我不理解你使用两个线程的理由。。。除非要求从输入流读取的所有字符都及时写入输出流。斯蒂芬:在写入之前,你无法读取某些内容。因此,只有一个线程,您要么需要先写入所有内容(创建一个大的内存阵列,如果希望避免),要么需要让它们交替使用,非常小心地让读卡器从不阻塞等待输入(因为如果他这样做,写卡器也永远无法执行)。在JEE环境中使用此建议安全吗?容器可能正在运行许多自己的线程?@Toskan如果
新线程
由于任何原因不适合您的容器,那么请查看是否有可以使用的线程池。在此处提供使用其类的教程将非常棒,但是,如果您在同一个线程中读写这些文件,可能会出现死锁。我希望他们能用NIO更新这个!这个想法很有趣,但遗憾的是,只有在您控制生成数据的代码的情况下,这才有效。如果另一个第三方库在不返回控制的情况下将GBs的数据写入OutputStream,那么您也可以将所有内容复制到内存中,这与此类的要点不同。
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);

}