Ta strona: http://blog.ostermiller.org/convert-java-outputstream-inputstream zawiera opis sposobu tworzenia InputStream z OutputStream:
new ByteArrayInputStream(out.toByteArray())
Inną alternatywą jest 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 wydajniej?
EDYTOWAĆ:
Za radą Laurence'a Gonsalvesa wypróbowałem PipedStreams i okazało się, że nie są one takie trudne. 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))
źródło
new Thread
z jakiegoś powodu nie jest odpowiedni w Twoim kontenerze, sprawdź, czy istnieje pula wątków, której możesz użyć.Istnieje inna biblioteka Open Source o nazwie EasyStream, która obsługuje rury i wątki w przejrzysty sposób. To nie jest naprawdę skomplikowane, jeśli wszystko pójdzie dobrze. Problemy pojawiają się, gdy (patrząc na przykład Laurence Gonsalves)
Zgłasza wyjątek. W tym przykładzie wątek po prostu się kończy, a wyjątek zostaje utracony, podczas gdy zewnętrzna część
InputStream
może zostać obcięta.Easystream radzi sobie z propagacją wyjątków i innymi nieprzyjemnymi problemami, które debuguję od około roku. (Jestem opiekunem biblioteki: oczywiście moje rozwiązanie jest najlepsze;)) Oto przykład, jak z niego korzystać:
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) } };
Jest też fajne wprowadzenie, w którym wyjaśniono wszystkie inne sposoby konwersji OutputStream na InputStream. Warto rzucić okiem.
źródło
Prostym rozwiązaniem, które pozwala 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); } }
W razie potrzeby napisz do powyższego strumienia wyjściowego, a następnie wywołaj,
toInputStream
aby uzyskać strumień wejściowy z bazowego buforu. Potraktuj strumień wyjściowy jako zamknięty po tym punkcie.źródło
Myślę, że najlepszym sposobem na połączenie InputStream z OutputStream są strumienie potokowe - dostępne w pakiecie java.io, w następujący sposób:
// 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 są dwie główne zalety tego kodu:
1 - Nie ma dodatkowego zużycia pamięci poza buforem.
2 - Nie musisz ręcznie obsługiwać kolejkowania danych
źródło
Zwykle staram się unikać tworzenia oddzielnego wątku ze względu na zwiększoną szansę na zakleszczenie, zwiększoną trudność w zrozumieniu kodu i problemy z obsługą wyjątków.
Oto moje proponowane rozwiązanie: ProducerInputStream, który tworzy zawartość w kawałkach przez powtarzające się wywołania funkcji bringChunk ():
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); }
źródło