Melhor maneira de ler um arquivo grande em uma matriz de bytes em c #?

Eu tenho um servidor web que irá ler arquivos binários grandes (vários megabytes) em matrizes de bytes. O servidor pode estar lendo vários arquivos ao mesmo tempo (solicitações de páginas diferentes), então estou procurando a maneira mais otimizada de fazer isso sem sobrecarregar muito o CPU. O código abaixo é bom o suficiente?

public byte[] FileToByteArray(string fileName) { byte[] buff = null; FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); BinaryReader br = new BinaryReader(fs); long numBytes = new FileInfo(fileName).Length; buff = br.ReadBytes((int) numBytes); return buff; } 

Simplesmente substitua a coisa toda por:

 return File.ReadAllBytes(fileName); 

No entanto, se você estiver preocupado com o consumo de memory, não deverá ler todo o arquivo na memory de uma só vez. Você deveria fazer isso em pedaços.

Eu poderia argumentar que a resposta aqui geralmente é “não”. A menos que você realmente precise de todos os dados de uma só vez, considere usar uma API baseada em Stream (ou alguma variante de leitor / iterador). Isso é especialmente importante quando você tem várias operações paralelas (conforme sugerido pela pergunta) para minimizar a carga do sistema e maximizar o rendimento.

Por exemplo, se você estiver transmitindo dados para um chamador:

 Stream dest = ... using(Stream source = File.OpenRead(path)) { byte[] buffer = new byte[2048]; int bytesRead; while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) { dest.Write(buffer, 0, bytesRead); } } 

Eu pensaria isso:

 byte[] file = System.IO.File.ReadAllBytes(fileName); 

Seu código pode ser fatorado para isso (em vez de File.ReadAllBytes):

 public byte[] ReadAllBytes(string fileName) { byte[] buffer = null; using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { buffer = new byte[fs.Length]; fs.Read(buffer, 0, (int)fs.Length); } return buffer; } 

Observe o Integer.MaxValue – limitação de tamanho de arquivo colocado pelo método Read. Em outras palavras, você só pode ler um pedaço de 2 GB de uma só vez.

Observe também que o último argumento para o FileStream é um tamanho de buffer.

Eu também sugiro ler sobre FileStream e BufferedStream .

Como sempre, um programa de amostra simples para o perfil mais rápido será mais benéfico.

Além disso, o hardware subjacente terá um grande efeito no desempenho. Você está usando discos rígidos baseados em servidor com grandes caches e uma placa RAID com cache de memory integrada? Ou você está usando uma unidade padrão conectada à porta IDE?

Dependendo da frequência das operações, do tamanho dos arquivos e do número de arquivos que você está visualizando, há outros problemas de desempenho a serem levados em consideração. Uma coisa a lembrar é que cada um dos seus arrays de bytes será lançado à mercê do coletor de lixo. Se você não estiver armazenando nenhum desses dados em cache, poderá acabar criando muito lixo e perdendo a maior parte do seu desempenho para % Tempo no GC . Se os pedaços forem maiores que 85K, você estará alocando o LOH (Large Object Heap), que exigirá uma coleção de todas as gerações para liberar (isso é muito caro e em um servidor interromperá toda a execução enquanto estiver acontecendo) ). Além disso, se você tiver uma tonelada de objects no LOH, poderá acabar com a fragmentação LOH (o LOH nunca é compactado), o que leva a um desempenho insatisfatório e a exceções de falta de memory. Você pode reciclar o processo assim que atingir um certo ponto, mas não sei se isso é uma prática recomendada.

O ponto é, você deve considerar o ciclo de vida completo do seu aplicativo antes de necessariamente ler todos os bytes na memory da maneira mais rápida possível ou você pode estar trocando desempenho de curto prazo pelo desempenho geral.

Eu diria que BinaryReader é bom, mas pode ser refatorado para isso, em vez de todas as linhas de código para obter o comprimento do buffer:

 public byte[] FileToByteArray(string fileName) { byte[] fileData = null; using (FileStream fs = File.OpenRead(fileName)) { using (BinaryReader binaryReader = new BinaryReader(fs)) { fileData = binaryReader.ReadBytes((int)fs.Length); } } return fileData; } 

Deve ser melhor do que usar .ReadAllBytes() , já que vi nos comentários a resposta principal que inclui .ReadAllBytes() que um dos comentadores tinha problemas com arquivos> 600 MB, já que um BinaryReader é destinado a esse tipo de coisa. Além disso, colocá-lo em uma instrução using garante que o FileStream e o BinaryReader sejam fechados e descartados.

Use a class BufferedStream em C # para melhorar o desempenho. Um buffer é um bloco de bytes na memory usado para armazenar dados em cache, reduzindo assim o número de chamadas para o sistema operacional. Os buffers melhoram o desempenho de leitura e gravação.

Veja o seguinte para um exemplo de código e uma explicação adicional: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

Eu recomendaria tentar o método Response.TransferFile() , em seguida, um Response.Flush() e Response.End() para servir seus arquivos grandes.

Se você estiver lidando com arquivos acima de 2 GB, verá que os methods acima falharão.

É muito mais fácil simplesmente entregar o stream para o MD5 e permitir que ele grave seu arquivo para você:

 private byte[] computeFileHash(string filename) { MD5 md5 = MD5.Create(); using (FileStream fs = new FileStream(filename, FileMode.Open)) { byte[] hash = md5.ComputeHash(fs); return hash; } }