Como você lê o mesmo fluxo de entrada duas vezes? É possível copiá-lo de alguma forma?
Preciso obter uma imagem da Web, salvá-la localmente e retornar a imagem salva. Eu apenas pensei que seria mais rápido usar o mesmo fluxo em vez de iniciar um novo fluxo no conteúdo baixado e depois lê-lo novamente.
java
inputstream
Warpzit
fonte
fonte
Respostas:
Você pode usar
org.apache.commons.io.IOUtils.copy
para copiar o conteúdo do InputStream para uma matriz de bytes e, em seguida, ler repetidamente da matriz de bytes usando um ByteArrayInputStream. Por exemplo:fonte
Dependendo da origem do InputStream, talvez não seja possível redefini-lo. Você pode verificar se
mark()
ereset()
é suportado usandomarkSupported()
.Se for, você pode chamar
reset()
o InputStream para retornar ao início. Caso contrário, você precisará ler o InputStream da fonte novamente.fonte
InputStream
subsclasses comoBufferedInputStream
suporta 'marca'se o seu
InputStream
suporte usando mark, então você podemark()
seu inputStream e entãoreset()
. se a suaInputStrem
marca não suportar, você poderá usar a classejava.io.BufferedInputStream
, para incorporar seu stream a algoBufferedInputStream
como estefonte
BufferedInputStream.fill()
, há a seção "buffer de expansão", onde o novo tamanho do buffer é comparado apenas amarklimit
eMAX_BUFFER_SIZE
.Você pode agrupar o fluxo de entrada com PushbackInputStream. PushbackInputStream permite não ler (" escrever de volta ") bytes que já foram lidos, para que você possa fazer assim:
Observe que PushbackInputStream armazena buffer interno de bytes para que ele realmente crie um buffer na memória que retenha os bytes "gravados de volta".
Conhecendo essa abordagem, podemos ir além e combiná-la com o FilterInputStream. FilterInputStream armazena o fluxo de entrada original como um representante. Isso permite criar uma nova definição de classe que permite " não ler " dados originais automaticamente. A definição desta classe é a seguinte:
Esta classe tem dois métodos. Um para ler no buffer existente (a definição é análoga à chamada
public int read(byte b[], int off, int len)
da classe InputStream). Segundo, que retorna um novo buffer (isso pode ser mais eficaz se o tamanho do buffer para leitura for desconhecido).Agora vamos ver nossa classe em ação:
fonte
Se você estiver usando uma implementação de
InputStream
, poderá verificar o resultado eInputStream#markSupported()
informar se pode ou não usar o métodomark()
/reset()
.Se você puder marcar o fluxo ao ler, ligue
reset()
para voltar para começar.Se não conseguir, será necessário abrir um fluxo novamente.
Outra solução seria converter InputStream em matriz de bytes e, em seguida, iterar sobre a matriz quantas vezes você precisar. Você pode encontrar várias soluções neste post Converter InputStream em matriz de bytes em Java usando bibliotecas de terceiros ou não. Cuidado, se o conteúdo lido for muito grande, você poderá ter alguns problemas de memória.
Por fim, se você precisar ler uma imagem, use:
O uso
ImageIO#read(java.net.URL)
também permite usar o cache.fonte
ImageIO#read(java.net.URL)
: alguns servidores da Web e CDNs podem rejeitar chamadas simples (ou seja, sem um Agente do Usuário que faça o servidor acreditar que a chamada é proveniente de um navegador da Web) feita porImageIO#read
. Nesse caso, usando aURLConnection.openConnection()
configuração do agente do usuário para essa conexão + usando `ImageIO.read (InputStream), na maioria das vezes, fará o truque.InputStream
não é uma interfaceE se:
fonte
Para dividir um
InputStream
em dois, evitando carregar todos os dados na memória e processá-los independentemente:OutputStream
, precisamente:PipedOutputStream
PipedInputStream
são os retornadosInputStream
.OutputStream
. Então, tudo o que é lido no sourcingInputStream
seria escrito em ambosOutputStream
. Não é necessário implementar isso, porque isso já é feito emTeeInputStream
(commons.io).Em um encadeamento separado, leia todo o inputStream da fonte e implicitamente os dados de entrada são transferidos para o inputStreams de destino.
Lembre-se de fechar o inputStreams depois de consumido e feche o encadeamento que é executado:
TeeInputStream.readAllBytes()
No caso, você precisa dividi-lo em vários
InputStream
, em vez de apenas dois. Substitua no fragmento anterior de código a classeTeeOutputStream
para sua própria implementação, que encapsularia aList<OutputStream>
e substituiria aOutputStream
interface:fonte
Converta o fluxo de entrada em bytes e passe-o para a função savefile onde você monta o mesmo no fluxo de entrada. Também na função original, use bytes para usar em outras tarefas
fonte
Caso alguém esteja executando um aplicativo Spring Boot, e você queira ler o corpo da resposta de um
RestTemplate
(e é por isso que desejo ler um fluxo duas vezes), existe uma maneira mais limpa de fazer isso.Primeiro de tudo, você precisa usar o Spring
StreamUtils
para copiar o fluxo para uma String:Mas isso não é tudo. Você também precisa usar uma fábrica de solicitações que possa armazenar em buffer o fluxo para você, assim:
Ou, se você estiver usando o bean de fábrica, então (este é o Kotlin, no entanto):
Fonte: https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-westout-destroying-the-body/
fonte
Se você estiver usando o RestTemplate para fazer chamadas http, basta adicionar um interceptador. O corpo da resposta é armazenado em cache pela implementação do ClientHttpResponse. Agora o fluxo de entrada pode ser recuperado da reposição quantas vezes for necessário
fonte