Самый эффективный способ создания InputStream из OutputStream - PullRequest
79 голосов
/ 04 августа 2009

Эта страница: http://blog.ostermiller.org/convert-java-outputstream-inputstream описывает, как создать InputStream из OutputStream:

new ByteArrayInputStream(out.toByteArray())

Другими альтернативами являются использование PipedStreams и новых потоков, что является громоздким.

Мне не нравится идея копировать много мегабайт в новый байтовый массив памяти. Есть ли библиотека, которая делает это более эффективно?

EDIT:

По совету Лоуренса Гонсалвеса я попробовал 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))

Ответы [ 5 ]

66 голосов
/ 04 августа 2009

Если вы не хотите копировать все данные в буфер в памяти сразу, вам понадобится ваш код, который использует 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);
15 голосов
/ 02 июня 2011

Существует еще одна библиотека с открытым исходным кодом, которая называется EasyStream , которая прозрачно обрабатывает каналы и потоки. Это не сложно, если все идет хорошо. Проблемы возникают, когда (глядя на пример Лоуренса Гонсалвеса)

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

Существует также хорошее введение , где объясняются все другие способы преобразования OutputStream в InputStream. Стоит взглянуть.

9 голосов
/ 01 мая 2016

Простое решение, позволяющее избежать копирования в буфер, - это создать специальное 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, чтобы получить входной поток через базовый буфер. Считайте выходной поток закрытым после этой точки.

6 голосов
/ 28 января 2015

Я думаю, что лучший способ подключить 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 - вам не нужно обрабатывать очереди данных вручную

1 голос
/ 28 ноября 2016

Обычно я стараюсь избегать создания отдельного потока из-за повышенной вероятности тупиковой ситуации, повышенной сложности понимания кода и проблем с обработкой исключений.

Вот мое предлагаемое решение: ProducerInputStream, который создает контент в чанках при повторных вызовах yieldChunk ():

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

}
...