Eu tenho um InputStream que passo para um método para fazer algum processamento. Usarei o mesmo InputStream em outro método, mas após o primeiro processamento, o InputStream parece ser fechado dentro do método.
Como posso clonar o InputStream para enviar para o método que o fecha? Existe outra solução?
EDIT: o método que fecha o InputStream é um método externo de uma lib. Eu não tenho controle sobre o fechamento ou não.
private String getContent(HttpURLConnection con) {
InputStream content = null;
String charset = "";
try {
content = con.getInputStream();
CloseShieldInputStream csContent = new CloseShieldInputStream(content);
charset = getCharset(csContent);
return IOUtils.toString(content,charset);
} catch (Exception e) {
System.out.println("Error downloading page: " + e);
return null;
}
}
private String getCharset(InputStream content) {
try {
Source parser = new Source(content);
return parser.getEncoding();
} catch (Exception e) {
System.out.println("Error determining charset: " + e);
return "UTF-8";
}
}
java
clone
inputstream
Renato Dinhani
fonte
fonte
Respostas:
Se tudo o que você deseja fazer é ler as mesmas informações mais de uma vez e os dados de entrada forem pequenos o suficiente para caber na memória, você poderá copiar os dados
InputStream
para um ByteArrayOutputStream .Em seguida, você pode obter a matriz de bytes associada e abrir quantos ByteArrayInputStream s "clonados" desejar.
Mas se você realmente precisar manter o fluxo original aberto para receber novos dados, precisará rastrear esse
close()
método externo e impedir que seja chamado de alguma forma.ATUALIZAÇÃO (2019):
Desde o Java 9, os bits do meio podem ser substituídos por
InputStream.transferTo
:fonte
TeeInputStream
como descrito na resposta aqui .Você quer usar o Apache
CloseShieldInputStream
:Este é um invólucro que impedirá que o fluxo seja fechado. Você faria algo assim.
fonte
CloseShield
não está funcionando porque seuHttpURLConnection
fluxo de entrada original está sendo fechado em algum lugar. Seu método não deveria chamar IOUtils com o fluxo protegidoIOUtils.toString(csContent,charset)
?close()
ligação, mas o fato de o Stream estar sendo lido até o fim. Comomark()
ereset()
talvez não sejam os melhores métodos para conexões http, talvez você deva dar uma olhada na abordagem de matriz de bytes descrita em minha resposta.Você não pode cloná-lo, e como você resolverá seu problema depende de qual é a fonte dos dados.
Uma solução é ler todos os dados do InputStream em uma matriz de bytes e criar um ByteArrayInputStream em torno dessa matriz de bytes e passar esse fluxo de entrada para o seu método.
Edit 1: Ou seja, se o outro método também precisar ler os mesmos dados. Ou seja, você deseja "redefinir" o fluxo.
fonte
Se os dados lidos no fluxo forem grandes, eu recomendaria o uso de um TeeInputStream do Apache Commons IO. Dessa forma, você pode essencialmente replicar a entrada e passar um canal t'd como seu clone.
fonte
Isso pode não funcionar em todas as situações, mas aqui está o que eu fiz: Estendi a classe FilterInputStream e faço o processamento necessário dos bytes quando a lib externa lê os dados.
Então você simplesmente passa uma instância de
StreamBytesWithExtraProcessingInputStream
onde você teria passado no fluxo de entrada. Com o fluxo de entrada original como parâmetro do construtor.Deve-se notar que isso funciona byte por byte, portanto, não o use se um alto desempenho for um requisito.
fonte
UPD. Verifique o comentário antes. Não é exatamente o que foi pedido.
Se você estiver usando,
apache.commons
poderá copiar fluxos usandoIOUtils
.Você pode usar o seguinte código:
Aqui está o exemplo completo adequado à sua situação:
Este código requer algumas dependências:
MAVEN
GRADLE
Aqui está a referência DOC para este método:
Você pode encontrar mais informações
IOUtils
aqui: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)fonte
Abaixo está a solução com Kotlin.
Você pode copiar seu InputStream no ByteArray
Se você precisar ler
byteInputStream
várias vezes, ligue parabyteInputStream.reset()
antes de ler novamente.https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/
fonte
A turma abaixo deve fazer o truque. Apenas crie uma instância, chame o método "multiplicar" e forneça o fluxo de entrada de origem e a quantidade de duplicatas necessária.
Importante: você deve consumir todos os fluxos clonados simultaneamente em threads separados.
fonte
A clonagem de um fluxo de entrada pode não ser uma boa ideia, pois isso requer conhecimento profundo sobre os detalhes do fluxo de entrada que está sendo clonado. Uma solução alternativa para isso é criar um novo fluxo de entrada que leia novamente a mesma fonte.
Portanto, usando alguns recursos do Java 8, seria assim:
Este método tem o efeito positivo de reutilizar o código que já está em vigor - a criação do fluxo de entrada encapsulado em
inputStreamSupplier
. E não há necessidade de manter um segundo caminho de código para a clonagem do fluxo.Por outro lado, se a leitura do fluxo for cara (porque é feita em uma conexão de baixa largura de banda), esse método dobrará os custos. Isso pode ser contornado usando um fornecedor específico que armazenará o conteúdo do fluxo localmente primeiro e fornecerá um
InputStream
recurso local agora.fonte
is
?java.io.File
oujava.net.URL
por exemplo para criar um novo fluxo de entrada cada vez que é chamado.