Como ler um arquivo grande com inputstream sem estourar memória

16 respostas
kleberaugus

Seguinte, estou tentando ler arquivos com inputstream, preciso lê-los e depois trata-los para depois gravar novamente, o problema é como vou ler estes arquivos, se eu colocar inputstream.read, vai ler byte por byte mas pelo que li isso é lento, então pensei ler vários bytes de uma vez em uma array, porém para arquivos grandes não sei se dá pra fazer uma array tão grande, uma pessoa falou para criar um buffer e me passou esse código:FileInputStream fis = newTexto pré-formatado`FileInputStream(new File(“C:/arquivo.dat”));

byte [] buffer = new byte[2048];
int readBytes = fis.read(buffer, 0, 2048)
while(readBytes > 0){
    readBytes = fis.read(buffer, 0, 2048);
}`

Porém não entendi esse código, como eu tenho acesso aos dados para guardá-los em variáveis e poder tratá-los? Não sei como acessar os dados, ver os dados…

16 Respostas

kleberaugus

Consegui lendo a array buffer mas e se eu quizer arquivos maiores que 2048 bytes? Tipo um arquivo de 1 giga?

lvbarbosa

Você precisa carregar o dado inteiro em memória para tratá-lo? Não pode ler pequenas partes, tratá-las, liberar a memória e em seguida ir para a próxima parte?

kleberaugus

É exatamente isso que vc falou que eu quero fazer, mas não sei como.

lvbarbosa

Pra isso você precisa saber qual é o tamanho de uma unidade de trabalho. Quantos bytes você precisa para ter algo com o qual pode trabalhar? O tamanho das unidades é fixo ou varia?

kleberaugus

Por exemplo, se eu leio 1000 bytes, como eu faço para na próxima vez recomeçar do 1001 e não do zero, e como eu faço isso sabendo o tamanho do arquivo todo?

kleberaugus

Eu quero tratar uns 5 bits por vez, mas pensei que baixar vários bytes de uma vez seria mais rápido.

lvbarbosa

Essa é exatamente a definição de uma Stream. Conforme você vai lendo, a stream vai sendo consumida. Quando você pega uma quantidade de bytes, você os tira da Stream. A próxima leitura vai devolver bytes diferentes dos primeiros. A Stream é sequencial por definição.

Um exemplo, digamos que a stream tem 10 bytes:

abcdefghij

Se você pedir 2 bytes para ela, a resposta vai ser: ab. A stream vai ficar dessa forma:

cdefghij

Se você pedir mais 2 bytes, a nova resposta vai ser: cd. A stream vai ficar assim:

efghij

E assim por diante, até chegar no final e quando você tentar ler ela te falar que leu -1 bytes.

kleberaugus

O problema é que nesse código que postei, se a array de buffer não tiver o número total de bytes do arquivo, ele não lê tudo, só lê o tanto de bytes que pôs, mas aí como eu faço pra ler o resto?

lvbarbosa

Mas você não precisa saber o tamanho inteiro do arquivo, jovem. O exemplo foi só para ilustrar como streams funcionam. Quando você cria uma FileInputStream, a JVM não copia o arquivo inteiro para memória. Ela pega apenas uma parte do arquivo. Conforme você vai consumindo a stream, ela vai carregando mais partes do arquivo na memória e colocando os novos bytes no final da stream. Mas você (seu código) não precisa saber desse detalhe. Trabalhe com a abstração InputStream, que é: uma sequência de bytes. Não interessa de onde esses bytes vem nem como eles estão sendo carregados.

O que você precisa saber é de quantos bytes você precisa para conseguir fazer alguma coisa com o dado.

Se o problema fosse, por exemplo, ler um arquivo cheio de números inteiros de 4 bytes, incrementar todos e salvar em um arquivo novo, seu buffer só precisa de 4 bytes. O algoritmo seria:

  1. Lê 4 bytes da stream (se a stream retornar que não leu nada, acaba o programa);
  2. Transforma isso num int;
  3. Incrementa o número;
  4. Escreve o resultado na stream de saída. Retorna para #1.

Tudo depende do que você está tentando resolver.

lvbarbosa

Um loop, enquanto a stream retorna dados você continua executando o algoritmo.

kleberaugus

Ah tá tinha me esquecido disso, eu tenho a seguinte linha: int readBytes = inputstream.read(buffer, 0, 2048); eu posso colocar no loop while(readbytes!=-1){}, Valeu, muito obrigado pelas respostas!

kleberaugus
Mas vendo aqui não deu certo, olha como está meu código:

byte [] buffer = new byte[2048];

int readBytes = inputstream.read(buffer, 0, 2048);

while(readBytes > 0){

readBytes = inputstream.read(buffer, 0, 2048);

for(int i = 0;i<2048;i++){

int decimal = buffer[i];

String binario = conversor.ConverterDecimalToBinario(decimal);

int decimal1 = conversor.ConverterBinarioToDecimal(binario);

outputstream.write(decimal1);//-----------------------------------------GRAVAÇÃO

}//FIM DO FOR

}//FIM DO WHILE

O problema é que eu tenho que definir o tamanho da array, e se eu coloco um tamanho menor que o tamanho total do arquivo, dá pau no arquivo de saída, ou seja, ele não lê tudo.

lvbarbosa

Ao invés de for(int i = 0;i<2048;i++), coloque for (int i = 0; i < readBytes; i++).

kleberaugus

ahnnnn MUITO OBRIGADO!

kleberaugus

Sem querer ser chato, mas já sendo: o que está ocorrendo agora é que estou lendo o arquivo original e reescrevendo sem nenhum tratamento, só pra ver se o código está certo, logo, o arquivo escrito deve ficar igual ao arquivo original, o problema é que o arquivo escrito está ficando exatamente 2048 bytes de tamanho, menor que o original, estou fazendo while (readBytes!=0)

kleberaugus

O arquivo final está exatamente 2048 bytes menor que o original!

Criado 17 de fevereiro de 2019
Ultima resposta 17 de fev. de 2019
Respostas 16
Participantes 2