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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1225909/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 01:08:01  来源:igfitidea点击:

Most efficient way to create InputStream from OutputStream

javaiostreaminputstreambytearrayoutputstream

提问by Vagif Verdi

This page: http://blog.ostermiller.org/convert-java-outputstream-inputstreamdescribes how to create an InputStream from OutputStream:

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

new ByteArrayInputStream(out.toByteArray())

Other alternatives are to use PipedStreams and new threads which is cumbersome.

其他替代方法是使用 PipedStreams 和新线程,这很麻烦。

I do not like the idea of copying many megabytes to new in memory byte array. Is there a library that does this more efficiently?

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

EDIT:

编辑:

By advice from Laurence Gonsalves, i tried PipedStreams and it turned out they are not that hard to deal with. Here's the sample code in clojure:

根据 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))

采纳答案by Laurence Gonsalves

If you don't want to copy all of the data into an in-memory buffer all at once then you're going to have to have your code that uses the OutputStream (the producer) and the code that uses the InputStream (the consumer) either alternate in the same thread, or operate concurrently in two separate threads. Having them operate in the same thread is probably much more complicated that using two separate threads, is much more error prone (you'll need to make sure that the consumer neverblocks waiting for input, or you'll effectively deadlock) and would necessitate having the producer and consumer running in the same loop which seems way too tightly coupled.

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

So use a second thread. It really isn't that complicated. The page you linked to had a perfect example:

所以使用第二个线程。真的没有那么复杂。您链接到的页面有一个完美的例子:

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

回答by Gab

There is another Open Source library called EasyStreamthat deals with pipes and thread in a transparent way. That isn't really complicated if everything goes well. Problems arise when (looking at Laurence Gonsalves example)

还有另一个名为EasyStream 的开源库,它以透明的方式处理管道和线程。如果一切顺利,这并不复杂。问题出现的时候(看看劳伦斯贡萨尔维斯的例子)

class1.putDataOnOutputStream(out);

class1.putDataOnOutputStream(out);

Throws an exception. In that example the thread simply completes and the exception is lost, while the outer InputStreammight be truncated.

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

Easystream deals with exception propagation and other nasty problems I've been debugging for about one year. (I'm the mantainer of the library: obviously my solution is the best one ;) ) Here is an example on how to use it:

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)
 }
};

There is also a nice introductionwhere all other ways to convert an OutputStream into an InputStream are explained. Worth to have a look.

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

回答by Mostafa Abdellateef

I think the best way to connect InputStream to an OutputStream is through piped streams- available in java.io package, as follow:

我认为将 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.*/

In my opinion there are two main advantages for this code:

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

1 - There is no additional consumption of memory except for the buffer.

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

2 - You don't need to handle data queuing manually

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

回答by Eron Wright

A simple solution that avoids copying the buffer is to create a special-purpose ByteArrayOutputStream:

避免复制缓冲区的一个简单解决方案是创建一个特殊用途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);
    }
}

Write to the above output stream as needed, then call toInputStreamto obtain an input stream over the underlying buffer. Consider the output stream as closed after that point.

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

回答by Mark

I usually try to avoid creating a separate thread because of the increased chance of deadlock, the increased difficulty of understanding the code, and the problems of dealing with exceptions.

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

Here's my proposed solution: a ProducerInputStream that creates content in chunks by repeated calls to produceChunk():

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

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);

}