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

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…

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

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?

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

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?

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?

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

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.

1 curtida

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?

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.

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

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!

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.

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

ahnnnn MUITO OBRIGADO!

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)…

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