Pregunta

Tengo un servidor web que lea archivos binarios grandes (varios megabytes) en matrices de bytes. El servidor podría estar leyendo varios archivos al mismo tiempo (diferentes solicitudes de página), por lo que estoy buscando la forma más optimizada para hacer esto sin gravar la CPU demasiado. 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;
}
¿Fue útil?

Solución

Sólo tiene que sustituir todo el asunto con:

return File.ReadAllBytes(fileName);

Sin embargo, si usted está preocupado por el consumo de memoria, usted debe no leer todo el archivo en memoria a la vez a todos. Usted debe hacer eso en trozos.

Otros consejos

Yo podría argumentar que la respuesta aquí general es "No". A menos que absolutamente necesario todos los datos a la vez, considerar el uso de una API basada en Stream (o alguna variante del lector / iterador). Eso es sobre todo importante cuando se tiene múltiples operaciones en paralelo (como se sugiere en la pregunta) para minimizar la carga del sistema y maximizar el rendimiento.

Por ejemplo, si se 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);
    }
}

Me gustaría pensar esto:

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

El código puede ser un factor a 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 el Integer.MaxValue - limitación de tamaño de archivo colocado por el método Read. En otras palabras, sólo se puede leer un trozo de 2 GB a la vez.

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

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

Como siempre un programa de ejemplo sencillo al perfil que es más rápido será más beneficioso.

También el hardware subyacente tendrá un gran efecto en el rendimiento. ¿Está utilizando unidades de disco duro basadas en servidor con grandes cachés y una tarjeta RAID con caché de memoria a bordo? O utilizando una unidad estándar conectado al puerto IDE?

En función de la frecuencia de las operaciones, el tamaño de los archivos, y el número de archivos que se encuentra en el mercado, existen otros problemas de rendimiento a tener en cuenta. Una cosa para recordar, es que cada una de las matrices de bytes se dará a conocer a merced del recolector de basura. Si usted no está cachear cualquiera de esos datos, se puede acabar creando una gran cantidad de basura y estar perdiendo la mayor parte de su rendimiento a % Tiempo en GC . Si los trozos son más grandes que 85K, se le asignación a los objetos grandes Montón (LOH) que requerirá una colección de todas las generaciones a liberar (esto es muy caro, y en el servidor va a parar toda la ejecución, mientras que está pasando ). Además, si usted tiene un montón de objetos en la LOH, puede terminar con la fragmentación LOH (LOH no se compacta), que conduce a un rendimiento inferior y excepciones de memoria insuficiente. Usted puede reciclar el proceso una vez que llega a cierto punto, pero no sé si eso es una buena práctica.

El punto es, usted debe considerar el ciclo de vida completo de su aplicación antes necesariamente sólo leer todos los bytes en la memoria de la forma más rápida posible o es posible que se la negociación rendimiento a corto plazo para el rendimiento general.

Yo diría BinaryReader está bien, pero se puede refactorizar a esto, en lugar de todas esas líneas de código para obtener la longitud de la memoria intermedia:

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

debe ser mejor que el uso de .ReadAllBytes(), desde que vi en los comentarios sobre la respuesta de la parte superior que incluye .ReadAllBytes() que uno de los comentaristas tenían problemas con los archivos> 600 MB, ya que un BinaryReader es para este tipo de cosas. Además, poniéndolo en un comunicado using asegura la FileStream y BinaryReader están cerrados y eliminados.

En caso de 'un gran archivo' se entiende más allá del límite de 4 GB, entonces mi lógica siguiente código escrito es apropiado. La cuestión clave a observar es el tipo de datos utilizado durante mucho tiempo con el método Seek. Como LARGA es capaz de señalar más allá de 2 ^ 32 límites de datos. En este ejemplo, el código está procesando primero procesar el archivo grande en trozos de 1 GB, después de que los grandes trozos enteros de 1 GB se procesan, los sobrantes (<1 GB) bytes se procesan. Yo uso este código con el cálculo de la CRC de los archivos más allá del tamaño de 4 GB. (Usando https://crc32c.machinezoo.com/ para el cálculo crc32c en este ejemplo)

private uint Crc32CAlgorithmBigCrc(string fileName)
{
    uint hash = 0;
    byte[] buffer = null;
    FileInfo fileInfo = new FileInfo(fileName);
    long fileLength = fileInfo.Length;
    int blockSize = 1024000000;
    decimal div = fileLength / blockSize;
    int blocks = (int)Math.Floor(div);
    int restBytes = (int)(fileLength - (blocks * blockSize));
    long offsetFile = 0;
    uint interHash = 0;
    Crc32CAlgorithm Crc32CAlgorithm = new Crc32CAlgorithm();
    bool firstBlock = true;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[blockSize];
        using (BinaryReader br = new BinaryReader(fs))
        {
            while (blocks > 0)
            {
                blocks -= 1;
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(blockSize);
                if (firstBlock)
                {
                    firstBlock = false;
                    interHash = Crc32CAlgorithm.Compute(buffer);
                    hash = interHash;
                }
                else
                {
                    hash = Crc32CAlgorithm.Append(interHash, buffer);
                }
                offsetFile += blockSize;
            }
            if (restBytes > 0)
            {
                Array.Resize(ref buffer, restBytes);
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(restBytes);
                hash = Crc32CAlgorithm.Append(interHash, buffer);
            }
            buffer = null;
        }
    }
    //MessageBox.Show(hash.ToString());
    //MessageBox.Show(hash.ToString("X"));
    return hash;
}

Utilice la clase BufferedStream en C # para mejorar el rendimiento. Un tampón es un bloque de bytes en la memoria que se utilizan para datos de la caché, lo que reduce el número de llamadas al sistema operativo. Los tampones mejoran el rendimiento de lectura y escritura.

Vea el siguiente ejemplo de código y explicaciones adicionales: http://msdn.microsoft.com/en-us/ biblioteca / system.io.bufferedstream.aspx

utilizar esto:

 bytesRead = responseStream.ReadAsync(buffer, 0, Length).Result;

Yo recomendaría probar el método Response.TransferFile() luego un Response.Flush() y Response.End() para servir a sus archivos de gran tamaño.

Si usted está tratando con archivos superiores a 2 GB, usted encontrará que los métodos anteriores fallan.

Es mucho más fácil sólo para entregar la corriente fuera a MD5 y permitir que a su trozo de archivos para 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;
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top