La meilleure façon de lire un gros fichier dans un tableau d'octets en C#?




file.readallbytes c# (8)

J'ai un serveur Web qui lira de gros fichiers binaires (plusieurs mégaoctets) dans des tableaux d'octets. Le serveur pourrait lire plusieurs fichiers en même temps (demandes de pages différentes), donc je suis à la recherche de la manière la plus optimisée de le faire sans trop taxer le CPU. Le code ci-dessous est-il assez bon?

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

Si vous avez affaire à des fichiers de plus de 2 Go, vous trouverez que les méthodes ci-dessus échouent.

Il est beaucoup plus facile de transférer le flux à MD5 et de le partager avec vous:

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

Selon la fréquence des opérations, la taille des fichiers et le nombre de fichiers que vous regardez, d'autres problèmes de performances doivent être pris en compte. Une chose à retenir, c'est que chacun de vos tableaux d'octets sera libéré à la merci du garbage collector. Si vous ne mettez pas en cache aucune de ces données, vous risquez de perdre beaucoup de vos données et de perdre la plupart de vos performances à % Time in GC . Si les blocs ont une taille supérieure à 85 Ko, vous allouerez au gros tas d'objets (LOH) qui nécessitera une collection de toutes les générations à libérer (c'est très cher, et sur un serveur, toute exécution sera stoppée pendant que ça continue) ). En outre, si vous avez une tonne d'objets sur le LOH, vous pouvez vous retrouver avec la fragmentation LOH (le LOH n'est jamais compacté) ce qui conduit à des performances médiocres et des exceptions de mémoire insuffisante. Vous pouvez recycler le processus une fois que vous avez atteint un certain point, mais je ne sais pas si c'est une bonne pratique.

Le fait est, vous devriez considérer le cycle de vie complet de votre application avant de simplement lire tous les octets dans la mémoire de la manière la plus rapide possible ou vous pourriez échanger des performances à court terme pour la performance globale.


Il suffit de remplacer le tout avec:

return File.ReadAllBytes(fileName);

Cependant, si vous êtes préoccupé par la consommation de mémoire, vous ne devriez pas lire tout le fichier en mémoire en une seule fois. Vous devriez le faire en morceaux.


Je penserais ceci:

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

Votre code peut être factorisé à ceci (au lieu 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;
} 

Notez Integer.MaxValue - limitation de taille de fichier placée par la méthode Read. En d'autres termes, vous ne pouvez lire qu'un bloc de 2 Go à la fois.

Notez également que le dernier argument de FileStream est une taille de tampon.

Je suggère également de lire sur FileStream et BufferedStream .

Comme toujours, un simple exemple de programme à profiler le plus rapide sera le plus bénéfique.

Votre matériel sous-jacent aura également un effet important sur les performances. Utilisez-vous des disques durs basés sur un serveur avec des caches volumineux et une carte RAID avec un cache mémoire intégré? Ou utilisez-vous un lecteur standard connecté au port IDE?


Je recommanderais d'essayer la méthode Response.TransferFile() puis un Response.Flush() et Response.End() pour servir vos fichiers volumineux.


Je pourrais faire valoir que la réponse ici est généralement "ne pas". À moins d' avoir absolument besoin de toutes les données à la fois, pensez à utiliser une API basée sur les Stream (ou une variante de lecteur / itérateur). Ceci est particulièrement important lorsque vous avez plusieurs opérations en parallèle (comme suggéré par la question) pour minimiser la charge du système et maximiser le débit.

Par exemple, si vous diffusez des données à un appelant:

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

Utilisez la classe BufferedStream en C # pour améliorer les performances. Un tampon est un bloc d'octets en mémoire utilisé pour mettre en cache des données, réduisant ainsi le nombre d'appels au système d'exploitation. Les tampons améliorent les performances de lecture et d'écriture.

Voir ce qui suit pour un exemple de code et une explication supplémentaire: BufferedStream







binary-data