Meilleur moyen de lire un fichier volumineux dans un tableau d’octets en C #?

J’ai un serveur Web qui lit les grands fichiers binarys (plusieurs mégaoctets) dans des tableaux d’octets. Le serveur peut lire plusieurs fichiers en même temps (différentes requêtes de page), donc je cherche le moyen le plus optimisé de le faire sans trop encombrer le processeur. Le code ci-dessous est-il suffisant?

public byte[] FileToByteArray(ssortingng 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; } 

Il suffit de remplacer le tout par:

 return File.ReadAllBytes(fileName); 

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

Je dirais que la réponse est généralement “non”. À moins que vous ayez absolument besoin de toutes les données à la fois, envisagez d’utiliser une API basée sur un Stream (ou une variante du lecteur / iterator). Cela est particulièrement important lorsque vous avez plusieurs opérations parallèles (comme suggéré par la question) pour minimiser la charge du système et optimiser le débit.

Par exemple, si vous transmettez 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); } } 

Je penserais ceci:

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

Votre code peut être associé à cela (au lieu de File.ReadAllBytes):

 public byte[] ReadAllBytes(ssortingng 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 le Integer.MaxValue – limitation de taille de fichier placée par la méthode Read. En d’autres termes, vous ne pouvez lire qu’un morceau de 2 Go à la fois.

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

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

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

De plus, votre matériel sous-jacent aura un impact important sur les performances. Utilisez-vous des disques durs basés sur serveur avec de grands caches et une carte RAID avec cache mémoire intégré? Ou utilisez-vous un lecteur standard connecté au port IDE?

En fonction de la fréquence des opérations, de la taille des fichiers et du nombre de fichiers que vous consultez, d’autres problèmes de performances doivent être pris en compte. Une chose à retenir est que chacun de vos tableaux d’octets sera publié à la merci du ramasse-miettes. Si vous ne mettez en cache aucune de ces données, vous risquez de créer beaucoup de déchets et de perdre la plupart de vos performances à % Time in GC . Si les blocs sont plus grands que 85K, vous allouerez à LOH (Large Object Heap) qui nécessitera une collection de toutes les générations pour libérer (c’est très cher, et sur un serveur arrêtera toute exécution pendant qu’il est en cours) ). De plus, si vous avez une tonne d’objects sur le LOH, vous pouvez vous retrouver avec une fragmentation LOH (le LOH n’est jamais compacté), ce qui entraîne de mauvaises performances 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 point est, vous devriez considérer le cycle de vie complet de votre application avant de lire nécessairement tous les octets en mémoire le plus rapidement possible ou vous pourriez être en train de négocier des performances à court terme pour la performance globale.

Je dirais que BinaryReader est BinaryReader , mais peut être modifié à la place de toutes ces lignes de code pour obtenir la longueur du tampon:

 public byte[] FileToByteArray(ssortingng 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 supérieure qui inclut .ReadAllBytes() qu’un des commentateurs avait des problèmes avec les fichiers> 600 Mo, car un BinaryReader est destiné à ce genre de chose. En outre, le placer dans une instruction using garantit que FileStream et BinaryReader sont fermés et éliminés.

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 les 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.

Consultez les informations suivantes pour obtenir un exemple de code et des explications supplémentaires: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

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

Si vous traitez des fichiers de plus de 2 Go, vous constaterez que les méthodes ci-dessus échouent.

Il est beaucoup plus simple de transmettre le stream à MD5 et de lui permettre de découper votre fichier:

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