Najbardziej efektywny sposób tworzenia strumienia wejściowego ze strumienia wyjściowego

Ta strona: http://blog.ostermiller.org/convert-java-outputstream-inputstream opisuje jak utworzyć strumień wejściowy z OutputStream:

new ByteArrayInputStream(out.toByteArray())

Inne alternatywy to użycie PipedStreams i nowych wątków, co jest uciążliwe.

Nie podoba mi się pomysł kopiowania wielu megabajtów do nowej tablicy bajtów pamięci. Czy istnieje biblioteka, która robi to bardziej efektywnie?

EDIT:

Dzięki radom Laurence ' a Gonsalvesa, wypróbowałem PipedStreams i okazało się nie są takie trudne do opanowania. Oto przykładowy kod w 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))
Author: Necreaux, 2009-08-04

5 answers

Jeśli nie chcesz kopiować wszystkich danych do bufora w pamięci jednocześnie, będziesz musiał mieć kod, który używa strumienia wyjściowego (producenta) i kodu, który używa strumienia wejściowego (konsumenta) albo naprzemiennie w tym samym wątku, lub działać jednocześnie w dwóch oddzielnych wątkach. Ich działanie w tym samym wątku jest prawdopodobnie o wiele bardziej skomplikowane, że używanie dwóch oddzielnych wątków jest o wiele bardziej podatne na błędy (musisz upewnić się, że konsument nigdy nie blokuje czekających na dane wejściowe, lub skutecznie zablokujesz) i wymagałoby to, aby producent i konsument działali w tej samej pętli, która wydaje się zbyt ściśle powiązana.

Więc użyj drugiego wątku. To naprawdę nie jest takie skomplikowane. Strona, do której podlinkowałeś, miała doskonały przykład:

  PipedInputStream in = new PipedInputStream();
  PipedOutputStream out = new PipedOutputStream(in);
  new Thread(
    new Runnable(){
      public void run(){
        class1.putDataOnOutputStream(out);
      }
    }
  ).start();
  class2.processDataFromInputStream(in);
 65
Author: Laurence Gonsalves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-27 08:23:22

Istnieje inna biblioteka Open Source o nazwie EasyStream który zajmuje się rurami i gwintem w przejrzysty sposób. To nie jest skomplikowane, jeśli wszystko pójdzie dobrze. Problemy pojawiają się, gdy (patrząc na przykład Laurence Gonsalves)

Klasa1.putDataOnOutputStream (out);

Rzuca wyjątek. W tym przykładzie wątek po prostu się kończy i wyjątek zostaje utracony, podczas gdy zewnętrzna InputStream może zostać obcięta.

Easystream zajmuje się propagacja wyjątków i inne paskudne problemy, które debuguję od około roku. (Jestem opiekunem biblioteki: oczywiście moje rozwiązanie jest najlepsze ;) ) Oto przykład jak go używać:

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

Istnieje również miłe wprowadzenie gdzie wyjaśniono wszystkie inne sposoby konwersji strumienia wyjściowego na strumień wejściowy. Warto zajrzeć.

 15
Author: Gab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-27 10:41:59

Prostym rozwiązaniem pozwalającym uniknąć kopiowania bufora jest utworzenie specjalnego przeznaczenia 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);
    }
}

Zapisz do powyższego strumienia wyjściowego w razie potrzeby, a następnie wywołaj toInputStream, aby uzyskać strumień wejściowy nad buforem bazowym. Uznaj strumień wyjściowy za zamknięty po tym punkcie.

 8
Author: Eron Wright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-01 18:58:38

Myślę, że najlepszym sposobem podłączenia strumienia wejściowego do strumienia wyjściowego jest strumieniowe strumienie - dostępne w java.io pakiet, jak następuje:

// 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.*/

Moim zdaniem istnieją dwie główne zalety tego kodu:

1 - nie ma dodatkowego zużycia pamięci poza buforem.

2-nie musisz obsługiwać kolejkowania danych ręcznie

 6
Author: Mostafa Abdellateef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-28 12:11:39

Zazwyczaj staram się unikać tworzenia osobnego wątku z powodu zwiększonej szansy na impas, zwiększonej trudności w zrozumieniu kodu i problemów z radzeniem sobie z wyjątkami.

Oto moje proponowane rozwiązanie: strumień ProducerInputStream, który tworzy zawartość w kawałkach przez powtarzające się wywołania do 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);

}
 1
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-28 13:19:45