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

j'ai un serveur web qui lira de gros fichiers binaires (plusieurs mégaoctets) dans des tableaux d'octets. Le serveur peut être en train de lire plusieurs fichiers en même temps (différentes requêtes de page), donc je cherche la manière la plus optimisée pour le faire sans trop imposer le CPU. Le code ci-dessous 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;
}
317
demandé sur Peter Mortensen 2010-01-09 00:24:02

9 réponses

il suffit de remplacer le tout par:

return File.ReadAllBytes(fileName);

cependant, si vous êtes préoccupé par la consommation de mémoire, vous devriez pas lire le fichier entier dans la mémoire tout à la fois à tous. Tu devrais le faire en morceaux.

649
répondu Mehrdad Afshari 2010-01-08 21:36:23

je pourrais soutenir que la réponse ici généralement est"ne pas". À moins que vous absolument besoin de toutes les données à la fois, envisager d'utiliser une API Stream basé (ou une variante de lecteur / itérateur). C'est-à-dire surtout important lorsque vous avez plusieurs opérations parallèles (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 à l'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);
    }
}
56
répondu Marc Gravell 2010-01-08 21:44:33

je pense ceci:

byte[] file = System.IO.File.ReadAllBytes(fileName);
29
répondu Powerlord 2010-01-08 21:28:44

votre code peut être pris en compte (au lieu du fichier.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 L'entier.MaxValue-limite de taille de fichier placée par la méthode Read. En d'autres termes vous ne pouvez lire un morceau de 2 go à la fois.

notez aussi que le dernier argument du FileStream est une taille de tampon.

je suggère aussi la lecture sur FileStream et BufferedStream .

comme toujours un exemple simple de programme à profiler qui est le plus rapide sera le plus bénéfique.

Également votre matériel sous-jacent aura un grand effet sur les performances. Utilisez-vous des disques durs basés sur le serveur avec de grandes caches et une carte RAID avec une mémoire cache embarquée? Ou utilisez-vous un lecteur standard connecté au port IDE?

22
répondu 2010-01-08 21:51:17

selon la fréquence des opérations, la taille des fichiers et le nombre de fichiers que vous regardez, il y a d'autres problèmes de performance à prendre en considération. Une chose à retenir, c'est que chacune de vos matrices de bytes sera libérée à la merci du ramasseur d'ordures. Si vous ne cachez aucune de ces données, vous pourriez finir par créer beaucoup de déchets et perdre la plupart de votre performance à % temps dans GC . Si les morceaux sont plus grands que 85K, vous allez allouer à la grande pile D'objets (LOH) qui va nécessiter une collection de toutes les générations pour libérer (c'est très cher, et sur un serveur va arrêter toute exécution pendant qu'il est en cours). De plus, si vous avez une tonne d'objets sur le LOH, vous pouvez finir avec la fragmentation du LOH (le LOH n'est jamais compacté) ce qui conduit à de mauvaises performances et des exceptions hors de la mémoire. 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 point est, vous devriez considérer le cycle de vie complet de votre application avant nécessairement juste la lecture de tous les octets en mémoire la manière la plus rapide possible ou vous pourriez être en train d'échanger des performances à court terme pour la performance globale.

9
répondu Joel 2010-01-08 22:25:19

je dirais BinaryReader est très bien, mais peut être remanié à cela, au lieu de toutes ces lignes de code pour obtenir la longueur du tampon:

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

devrait être mieux que d'utiliser .ReadAllBytes() , car j'ai vu dans les commentaires sur la réponse du haut qui inclut .ReadAllBytes() que l'un des commentateurs avait des problèmes avec les fichiers > 600 Mo, depuis un BinaryReader est destiné à ce genre de chose. En outre, en le mettant dans un using déclaration assure le FileStream et BinaryReader sont fermés et éliminés.

4
répondu vapcguy 2018-02-09 17:38:56

utilisez la classe BufferedStream en C# pour améliorer les performances. Un tampon est un bloc d'octets dans la mémoire pour le cache de 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 ci-dessous pour un exemple de code et une explication supplémentaire.: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

0
répondu Todd Moses 2010-01-08 21:37:45

je recommande d'essayer la méthode Response.TransferFile() puis une Response.Flush() et Response.End() pour servir vos gros fichiers.

-3
répondu Dave 2016-11-08 17:01:49

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

il est beaucoup plus facile de passer le flux à MD5 et de permettre que pour découper votre fichier pour 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;
    }
}
-7
répondu elaverick 2015-06-26 19:10:28