Вопрос

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

new ByteArrayInputStream(out.toByteArray())

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

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

РЕДАКТИРОВАТЬ:

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

Решение

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

Другие советы

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

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.Стоит посмотреть.

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

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

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

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

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

}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top