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