using System; using System.Collections.Generic; using System.IO; using System.Linq; using Developpez.Dotnet.Properties; namespace Developpez.Dotnet.IO { /// /// Fournit des méthodes d'extension pour les flux /// public static class StreamExtensions { private const int DefaultBufferSize = 4096; // Contournement d'un bug dans Resharper: la directive #if entre le commentaire de doc et la // méthode provoque ces deux avertissements (mais le comportement du compilateur est correct) #pragma warning disable 1587 #pragma warning disable 1591 /// /// Copie le contenu d'un flux vers le flux spécifié, en utilisant un buffer de 4096 octets. /// /// le flux d'origine /// le flux de destination /// le nombre d'octets copiés // La méthode CopyTo a été ajoutée dans .NET 4.0 et ne sera donc plus nécessaire // Par contre elle est utilisée en interne et doit donc rester présente #if DOTNET4 private #else public #endif static int CopyTo(this Stream fromStream, Stream toStream) { return CopyTo(fromStream, toStream, DefaultBufferSize); } /// /// Copie le contenu d'un flux vers le flux spécifié, en utilisant un buffer de la taille spécifiée. /// /// le flux d'origine /// le flux de destination /// taille du buffer /// le nombre d'octets copiés // La méthode CopyTo a été ajoutée dans .NET 4.0 et ne sera donc plus nécessaire // Par contre elle est utilisée en interne et doit donc rester présente #if DOTNET4 private #else public #endif static int CopyTo(this Stream fromStream, Stream toStream, int bufferSize) { fromStream.CheckArgumentNull("fromStream"); toStream.CheckArgumentNull("toStream"); int totalBytes = 0; byte[] buffer = new byte[bufferSize]; int nRead = 0; while ((nRead = fromStream.Read(buffer, 0, buffer.Length)) > 0) { toStream.Write(buffer, 0, nRead); totalBytes += nRead; } return totalBytes; } #pragma warning restore 1591 #pragma warning restore 1587 /// /// Copie le contenu d'un flux vers le fichier spécifié, en utilisant un buffer de 4096 octets. /// /// le flux d'origine /// le chemin du fichier de destination /// le nombre d'octets copiés public static int CopyToFile(this Stream fromStream, string path) { return fromStream.CopyToFile(path, DefaultBufferSize); } /// /// Copie le contenu d'un flux vers le fichier spécifié, en utilisant un buffer de la taille spécifiée. /// /// le flux d'origine /// le chemin du fichier de destination /// taille du buffer /// le nombre d'octets copiés public static int CopyToFile(this Stream fromStream, string path, int bufferSize) { fromStream.CheckArgumentNull("fromStream"); path.CheckArgumentNull("path"); using (FileStream toStream = File.OpenWrite(path)) { return CopyTo(fromStream, toStream, bufferSize); } } /// /// Lit tout le contenu du flux et le renvoie sous forme d'un tableau d'octets. /// ///Le flux à lire ///Un tableau d'octets contenant les données lues à partir du flux. public static byte[] ReadAllBytes(this Stream stream) { stream.CheckArgumentNull("stream"); using (var tmp = new MemoryStream()) { stream.CopyTo(tmp); return tmp.ToArray(); } } /// /// Lit le nombre d'octets spécifié et les renvoie sous forme d'un tableau d'octets. /// /// Le flux à lire /// Le nombre d'octets à lire /// Un tableau contenant les octets lus. /// Cette méthode bloque jusqu'à ce qu'elle ait lu le nombre d'octets demandé ou que la fin du flux soit atteinte. public static byte[] ReadBytes(this Stream stream, int count) { stream.CheckArgumentNull("stream"); count.CheckArgumentOutOfRange("count", 0, int.MaxValue); byte[] buffer = new byte[count]; int nRead, totalRead = 0; while (totalRead < count && (nRead = stream.Read(buffer, totalRead, count - totalRead)) != 0) { totalRead += nRead; } if (count > totalRead) { Array.Resize(ref buffer, totalRead); } return buffer; } // TODO: nom à revoir (SuppressClose, KeepOpen ?) /// /// Renvoie un wrapper autour d'un flux, qui ignore les demandes de fermeture de façon à éviter la fermeture /// du flux lorsque le reader ou writer qui travaille dessus est fermé /// /// Le flux à wrapper /// Un wrapper non-fermable autour de ce flux public static Stream AsNonClosing(this Stream stream) { stream.CheckArgumentNull("stream"); return new NonClosingStreamWrapper(stream); } /// /// Crée un wrapper qui reproduit la sortie d'un flux sur le ou les autres flux spécifiés. /// /// Premier flux /// Second flux /// Flux supplémentaires /// Un wrapper qui reproduit la sortie de stream sur les autres flux. public static Stream Tee(this Stream stream, Stream other, params Stream[] others) { var outputStreams = new[] { stream, other }.Concat(others).ToArray(); if (outputStreams.Any(w => w == null)) throw new ArgumentNullException(); if (outputStreams.Any(w => !w.CanWrite)) throw new ArgumentException(ExceptionMessages.AllStreamsMustBeWritable); return new TeeStream(outputStreams); } // TODO: nom à revoir (GetBytes ?) /// /// Renvoie une séquence d'octets lus à partir d'un flux. /// /// Flux à partir duquel lire les données /// Taille de buffer à utiliser pour la lecture /// La séquence d'octets lus à partir du flux public static IEnumerable AsByteEnumerable(this Stream stream, int bufferSize) { stream.CheckArgumentNull("stream"); bufferSize.CheckArgumentOutOfRange("bufferSize", 0, 1024 * 1024 * 1024); byte[] buffer = new byte[bufferSize]; int nRead = 0; while ((nRead = stream.Read(buffer, 0, bufferSize)) > 0) { for (int i = 0; i < nRead; i++) { yield return buffer[i]; } } } /// /// Renvoie une séquence d'octets lus à partir d'un flux. /// /// Flux à partir duquel lire les données /// La séquence d'octets lus à partir du flux public static IEnumerable AsByteEnumerable(this Stream stream) { return stream.AsByteEnumerable(DefaultBufferSize); } // TODO: nom à revoir (GetBlocks ?) /// /// Renvoie une séquence de blocs d'octets lus à partir d'un flux. /// /// Flux à partir duquel lire les données /// Taille de bloc désirée /// Une séquence de blocs d'octets de la taille spécifiée, lus à partir du flux ; le dernier bloc renvoyé peut être plus court si la fin du flux a été atteinte. public static IEnumerable AsBlockEnumerable(this Stream stream, int blockSize) { stream.CheckArgumentNull("stream"); blockSize.CheckArgumentOutOfRange("blockSize", 0, 1024 * 1024 * 1024); byte[] buffer = new byte[blockSize]; int nRead = 0; while ((nRead = stream.Read(buffer, 0, blockSize)) > 0) { byte[] buf2 = new byte[nRead]; Array.Copy(buffer, buf2, nRead); yield return buf2; } } } }