¿La mejor manera de leer un archivo grande en una matriz de bytes en C #?

Tengo un servidor web que leerá grandes archivos binarios (varios megabytes) en matrices de bytes. El servidor podría leer varios archivos al mismo tiempo (diferentes solicitudes de página), por lo que estoy buscando la forma más optimizada para hacerlo sin tener que cargar demasiado con la CPU. ¿El código a continuación es lo suficientemente bueno?

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; } 

Simplemente reemplace todo con:

 return File.ReadAllBytes(fileName); 

Sin embargo, si le preocupa el consumo de memoria, no debe leer todo el archivo en la memoria de una sola vez. Deberías hacer eso en pedazos.

Podría argumentar que la respuesta aquí en general es “do not”. A menos que necesites absolutamente todos los datos a la vez, considera usar una API basada en Stream (o alguna variante de lector / iterador). Esto es especialmente importante cuando tiene múltiples operaciones paralelas (como lo sugiere la pregunta) para minimizar la carga del sistema y maximizar el rendimiento.

Por ejemplo, si está transmitiendo datos a una persona que llama:

 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); } } 

Yo pensaría esto:

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

Su código puede tenerse en cuenta para esto (en lugar 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; } 

Tenga en cuenta la limitación del tamaño del archivo Integer.MaxValue por el método Read. En otras palabras, solo puede leer un fragmento de 2 GB a la vez.

También tenga en cuenta que el último argumento para FileStream es un tamaño de búfer.

También sugeriría leer sobre FileStream y BufferedStream .

Como siempre, un progtwig de muestra simple para crear un perfil que sea más rápido será lo más beneficioso.

Además, su hardware subyacente tendrá un gran efecto en el rendimiento. ¿Está utilizando unidades de disco duro basadas en servidor con cachés grandes y una tarjeta RAID con memoria caché incorporada? ¿O está utilizando una unidad estándar conectada al puerto IDE?

Dependiendo de la frecuencia de las operaciones, el tamaño de los archivos y el número de archivos que está viendo, hay otros problemas de rendimiento a tener en cuenta. Una cosa para recordar, es que cada una de tus matrices de bytes se liberará a merced del recolector de basura. Si no está almacenando en caché ninguno de esos datos, podría terminar creando una gran cantidad de basura y perder la mayor parte de su rendimiento en % de tiempo en GC . Si los fragmentos son mayores que 85K, se asignará al Large Object Heap (LOH) que requerirá una colección de todas las generaciones para liberarse (esto es muy caro, y en un servidor se detendrá toda la ejecución mientras está en marcha). ) Además, si tiene una tonelada de objetos en el LOH, puede terminar con la fragmentación de LOH (el LOH nunca se compacta) lo que conduce a un mal rendimiento y a excepciones de falta de memoria. Puedes reciclar el proceso una vez que llegas a cierto punto, pero no sé si esa es una mejor práctica.

El punto es que debes considerar el ciclo de vida completo de tu aplicación antes de necesariamente leer todos los bytes en la memoria de la manera más rápida posible o de cambiar el rendimiento a corto plazo por el rendimiento general.

Yo diría que BinaryReader está bien, pero se puede refactorizar a esto, en lugar de todas esas líneas de código para obtener la longitud del 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; } 

Debería ser mejor que usar .ReadAllBytes() , ya que vi en los comentarios en la respuesta superior que incluye a .ReadAllBytes() que uno de los comentaristas tuvo problemas con archivos> 600 MB, ya que un BinaryReader está destinado a este tipo de cosas. Además, ponerlo en una statement de using garantiza que FileStream y BinaryReader se cierren y eliminen.

Use la clase BufferedStream en C # para mejorar el rendimiento. Un búfer es un bloque de bytes en la memoria utilizado para almacenar en caché los datos, lo que reduce el número de llamadas al sistema operativo. Los búferes mejoran el rendimiento de lectura y escritura.

Consulte lo siguiente para obtener un ejemplo de código y una explicación adicional: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

Recomiendo probar el método Response.Flush() y luego un Response.Flush() y Response.End() para publicar sus archivos de gran tamaño.

Si está tratando con archivos de más de 2 GB, encontrará que los métodos anteriores fallan.

Es mucho más fácil simplemente entregar la secuencia de flujo a MD5 y permitir que el paquete por usted:

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