Esta página: http://blog.ostermiller.org/convert-java-outputstream-inputstream descreve como criar um InputStream a partir de OutputStream:
new ByteArrayInputStream(out.toByteArray())
Outras alternativas são usar PipedStreams e novos threads, o que é complicado.
Não gosto da ideia de copiar muitos megabytes para um novo array de bytes de memória. Existe uma biblioteca que faz isso de forma mais eficiente?
EDITAR:
A conselho de Laurence Gonsalves, experimentei o PipedStreams e descobri que não são tão difíceis de lidar. Aqui está o código de amostra em 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))
fonte
new Thread
não for apropriado em seu contêiner por qualquer motivo, então veja se há um pool de threads que você pode usar.Existe outra biblioteca Open Source chamada EasyStream que lida com tubos e threads de forma transparente. Isso não é realmente complicado se tudo correr bem. Os problemas surgem quando (olhando para o exemplo de Laurence Gonsalves)
Lança uma exceção. Nesse exemplo, o encadeamento simplesmente é concluído e a exceção é perdida, enquanto o externo
InputStream
pode ser truncado.Easystream lida com propagação de exceções e outros problemas desagradáveis que venho depurando há cerca de um ano. (Eu sou o mantenedor da biblioteca: obviamente minha solução é a melhor;)) Aqui está um exemplo de como usá-la:
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) } };
Há também uma boa introdução onde todas as outras maneiras de converter um OutputStream em InputStream são explicadas. Vale a pena dar uma olhada.
fonte
Uma solução simples que evita copiar o buffer é criar um propósito especial
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); } }
Grave no fluxo de saída acima conforme necessário e, em seguida, chame
toInputStream
para obter um fluxo de entrada no buffer subjacente. Considere o fluxo de saída como fechado após esse ponto.fonte
Acho que a melhor maneira de conectar InputStream a OutputStream é por meio de streams piped - disponível no pacote java.io, da seguinte maneira:
// 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.*/
Na minha opinião, existem duas vantagens principais para este código:
1 - Não há consumo adicional de memória, exceto para o buffer.
2 - Você não precisa lidar com o enfileiramento de dados manualmente
fonte
Geralmente, tento evitar a criação de um thread separado por causa do aumento da chance de conflito, da maior dificuldade de compreensão do código e dos problemas de lidar com exceções.
Esta é a minha solução proposta: um ProducerInputStream que cria conteúdo em partes por chamadas repetidas paraproduzirChunk ():
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); }
fonte