Domanda

Ho un server web che leggerà grandi file binari (diversi megabyte) in array di byte. Il server potrebbe essere la lettura più file contemporaneamente (diverse richieste di pagine), quindi non vedo il modo più ottimizzato per fare questo senza tassare la CPU troppo. È il codice qui sotto abbastanza buono?

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;
}
È stato utile?

Soluzione

Basta sostituire il tutto con:

return File.ReadAllBytes(fileName);

Tuttavia, se siete preoccupati per il consumo di memoria, è necessario non leggere l'intero file in memoria tutto in una volta a tutti. Si dovrebbe farlo a pezzi.

Altri suggerimenti

Potrei sostenere che la risposta qui in generale è "no". A meno che assolutamente bisogno tutti i dati in una sola volta, è consigliabile utilizzare un'API Stream-based (o qualche variante di lettore / iteratore). Questo è il specialmente importante quando si dispone di più operazioni in parallelo (come suggerito dalla domanda) per ridurre al minimo il carico del sistema e massimizzare il throughput.

Ad esempio, se si sta streaming di dati ad un chiamante:

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

Vorrei pensare questo:

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

Il codice può essere preso in considerazione a questo (al posto di 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;
} 

Si noti l'Integer.MaxValue - limitazione di dimensione del file da parte del metodo Read. In altre parole è possibile leggere solo un pezzo da 2 GB in una sola volta.

Si noti inoltre che l'ultimo argomento del FileStream è una dimensione di buffer.

Vorrei anche suggerire la lettura su FileStream BufferedStream .

Come sempre un semplice programma di esempio al profilo che è più veloce sarà più vantaggioso.

Anche l'hardware sottostante avrà un grande effetto sulle prestazioni. Si sta utilizzando le unità disco rigido basate su server con grandi cache e una scheda RAID con memoria cache a bordo? Oppure utilizza un'unità standard collegato alla porta IDE?

A seconda della frequenza delle operazioni, la dimensione dei file e il numero di file che stai guardando, ci sono altri problemi di prestazioni da prendere in considerazione. Una cosa da ricordare, è che ognuno dei vostri array di byte sarà rilasciato in balia del garbage collector. Se non stai caching qualsiasi di tali dati, si potrebbe finire per creare un sacco di immondizia e di essere di perdere la maggior parte della tua performance di % tempo in GC . Se i pezzi sono più grandi di 85K, sarete di ripartizione tra gli Large Object Heap (LOH) che richiederà una raccolta di tutte le generazioni per liberare (questo è molto costoso, e sul server smetterà di tutta l'esecuzione mentre è in corso ). Inoltre, se si dispone di una tonnellata di oggetti sul LOH, si può finire con LOH frammentazione (la LOH non è mai compattato) che porta a scarso rendimento e di eccezioni di memoria. È possibile riciclare il processo una volta si colpisce un certo punto, ma non so se questo è una delle migliori pratiche.

Il punto è, si dovrebbe prendere in considerazione l'intero ciclo di vita della vostra applicazione prima necessariamente solo leggere tutti i byte in memoria il modo più veloce possibile o si potrebbe essere negoziazione performance a breve termine per le prestazioni generali.

Direi BinaryReader va bene, ma può essere refactoring per questo, invece di tutte quelle righe di codice per ottenere la lunghezza 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;
}

dovrebbe essere migliore rispetto all'utilizzo di .ReadAllBytes(), da quando ho visto nei commenti sulla risposta superiore che include .ReadAllBytes() che uno dei commentatori ha avuto problemi con i file> 600 MB, dal momento che un BinaryReader è pensato per questo genere di cose. Inoltre, mettendolo in un comunicato using assicura la FileStream e BinaryReader sono chiusi e smaltiti.

In caso di 'un file di grandi dimensioni' si intende oltre il limite di 4 GB, allora la mia logica seguente codice scritto è appropriato. La questione chiave da notare è il tipo di dati a lungo utilizzato con il metodo Seek. Come una lunga è in grado di puntare al di là di 2 ^ 32 confini di dati. In questo esempio, il codice elabora prima elaborazione del file di grandi dimensioni in blocchi di 1 GB, dopo le grandi interi pezzi 1 GB vengono elaborati, i (<1GB) byte rimasti vengono elaborati. Io uso questo codice con il calcolo del CRC dei file al di là della dimensione di 4 GB. (Utilizzando https://crc32c.machinezoo.com/ per il calcolo crc32c in questo esempio)

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

Utilizzare la classe BufferedStream in C # per migliorare le prestazioni. Un buffer è un blocco di byte in memoria utilizzati per i dati di cache, riducendo così il numero di chiamate al sistema operativo. Buffer migliorano le prestazioni di lettura e scrittura.

Vedere il seguente per un esempio di codice e la spiegazione aggiuntiva: http://msdn.microsoft.com/en-us/ biblioteca / system.io.bufferedstream.aspx

utilizzare questo:

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

Vorrei consigliamo di provare il metodo Response.TransferFile() poi un Response.Flush() e Response.End() per servire i vostri file di grandi dimensioni.

Se hai a che fare con i file superiori a 2 GB, troverete che i metodi precedenti non riescono.

E 'molto più facile solo a portata di mano il flusso fuori ad MD5 e permettere che pezzo il file per voi:

private byte[] computeFileHash(string filename)
{
    MD5 md5 = MD5.Create();
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        byte[] hash = md5.ComputeHash(fs);
        return hash;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top