fichero - ¿La mejor manera de leer un archivo grande en una matriz de bytes en C#?
file to byte array c# (9)
Dependiendo de la frecuencia de las operaciones, el tamaño de los archivos y la cantidad de archivos que está viendo, hay otros problemas de rendimiento que deben tenerse en cuenta. Una cosa para recordar, es que cada uno de sus arreglos de bytes se liberará a merced del recolector de basura. Si no almacena en caché ninguno de esos datos, podría terminar creando una gran cantidad de basura y perderá la mayor parte de su rendimiento a % Time en GC . Si los trozos son más grandes que 85K, se asignará al Large Object Heap (LOH), que requerirá una colección de todas las generaciones para liberar (esto es muy costoso, y en un servidor detendrá toda la ejecución mientras se lleva a cabo ). Además, si tiene una tonelada de objetos en la LOH, puede terminar con la fragmentación de la LOH (la LOH nunca se compacta) lo que conduce a un rendimiento deficiente y excepciones fuera de la memoria. Puede reciclar el proceso una vez que llegue a cierto punto, pero no sé si es una buena práctica.
El punto es que debe considerar el ciclo de vida completo de su aplicación antes de leer necesariamente todos los bytes en la memoria de la manera más rápida posible o podría estar cambiando el rendimiento a corto plazo por el rendimiento general.
Tengo un servidor web que leerá archivos binarios grandes (varios megabytes) en matrices de bytes. El servidor podría estar leyendo varios archivos al mismo tiempo (solicitudes de página diferentes), por lo que estoy buscando la forma más optimizada de hacerlo sin afectar demasiado a la CPU. ¿Es el código de abajo 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;
}
Pensaría esto:
byte[] file = System.IO.File.ReadAllBytes(fileName);
Podría argumentar que la respuesta aquí generalmente es "no". A menos que necesite absolutamente todos los datos a la vez, considere usar una API basada en Stream
(o alguna variante de lector / iterador). Esto es especialmente importante cuando tiene varias 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);
}
}
Recomendaría probar el método Response.Flush()
y luego Response.Flush()
y Response.End()
para servir sus archivos grandes.
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 transmisión a MD5 y permitir que haga pedazos su archivo:
private byte[] computeFileHash(string filename)
{
MD5 md5 = MD5.Create();
using (FileStream fs = new FileStream(filename, FileMode.Open))
{
byte[] hash = md5.ComputeHash(fs);
return hash;
}
}
Simplemente reemplaza 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 vez. Usted debe hacer eso en trozos.
Su código puede ser factorizado 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;
}
Observe el valor de Integer.MaxValue: el tamaño del archivo colocado por el método de lectura. En otras palabras, solo puedes leer un fragmento de 2GB a la vez.
También tenga en cuenta que el último argumento de FileStream es un tamaño de búfer.
También sugeriría leer sobre FileStream y BufferedStream .
Como siempre, un programa de ejemplo simple para perfilar lo más rápido será lo más beneficioso.
También 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 caché de memoria integrada? ¿O está utilizando una unidad estándar conectada al puerto IDE?
Use la clase BufferedStream en C # para mejorar el rendimiento. Un búfer es un bloque de bytes en la memoria que se utiliza para almacenar datos en caché, lo que reduce el número de llamadas al sistema operativo. Los buffers mejoran el rendimiento de lectura y escritura.
Vea lo siguiente para un ejemplo de código y una explicación adicional: BufferedStream
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 búfer:
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 de la respuesta principal que incluye .ReadAllBytes()
que uno de los comentaristas tuvo problemas con los archivos> 600 MB, ya que un BinaryReader
está diseñado para este tipo de cosas. Además, ponerlo en una declaración de using
garantiza que FileStream
y BinaryReader
estén cerrados y eliminados.