2009-07-03 13 views
305

Existe-t-il un moyen simple ou une méthode pour convertir un Stream en un byte[] en C#?Comment convertir un flux en octet [] en C#?

+3

Pas vraiment la réponse à la question mais si votre flux provient d'un fichier, vous pouvez utiliser 'File.ReadAllBytes (path)' pour obtenir le tableau d'octets dans une ligne. –

Répondre

140

Appel fonction suivante comme

byte[] m_Bytes = StreamHelper.ReadToEnd (mystream); 

Fonction:

public static byte[] ReadToEnd(System.IO.Stream stream) 
    { 
     long originalPosition = 0; 

     if(stream.CanSeek) 
     { 
      originalPosition = stream.Position; 
      stream.Position = 0; 
     } 

     try 
     { 
      byte[] readBuffer = new byte[4096]; 

      int totalBytesRead = 0; 
      int bytesRead; 

      while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0) 
      { 
       totalBytesRead += bytesRead; 

       if (totalBytesRead == readBuffer.Length) 
       { 
        int nextByte = stream.ReadByte(); 
        if (nextByte != -1) 
        { 
         byte[] temp = new byte[readBuffer.Length * 2]; 
         Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length); 
         Buffer.SetByte(temp, totalBytesRead, (byte)nextByte); 
         readBuffer = temp; 
         totalBytesRead++; 
        } 
       } 
      } 

      byte[] buffer = readBuffer; 
      if (readBuffer.Length != totalBytesRead) 
      { 
       buffer = new byte[totalBytesRead]; 
       Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead); 
      } 
      return buffer; 
     } 
     finally 
     { 
      if(stream.CanSeek) 
      { 
       stream.Position = originalPosition; 
      } 
     } 
    } 
+19

Je ne suis pas certain d'être d'accord avec la politique d'expansion du tampon Length * 2. –

+0

Oui c'est vrai. Mais j'utilise ce code dans les frameworks 1.1 et 2.0. Parce que c'est si grand. Votre réponse est bonne. – pedrofernandes

+1

Si vous voulez être capable de lire des flux de longueur arbitraire, à peu près tout ce qui est nécessaire. Vous pouvez utiliser une liste et enregistrer du code .. – Thorarin

1
Stream s; 
int len = (int)s.Length; 
byte[] b = new byte[len]; 
int pos = 0; 
while((r = s.Read(b, pos, len - pos)) > 0) { 
    pos += r; 
} 

Une solution un peu plus compliquée est necesary est s.Length dépasse Int32.MaxValue. Mais si vous avez besoin de lire un flux aussi important dans la mémoire, vous pouvez envisager une approche différente de votre problème.

Modifier: Si votre flux ne prend pas en charge la propriété Length, modifiez-le à l'aide du workaround d'Earwicker.

public static class StreamExtensions { 
    // Credit to Earwicker 
    public static void CopyStream(this Stream input, Stream output) { 
     byte[] b = new byte[32768]; 
     int r; 
     while ((r = input.Read(b, 0, b.Length)) > 0) { 
      output.Write(b, 0, r); 
     } 
    } 
} 

[...] 

Stream s; 
MemoryStream ms = new MemoryStream(); 
s.CopyStream(ms); 
byte[] b = ms.GetBuffer(); 
+0

Ce serait génial si elle disait Read au lieu de Write! –

+0

Il a dit lire. Il voulait convertir le flux en byte [], qui est une lecture, pas une écriture. –

+0

Un autre problème avec ceci (je viens juste de me rappeler) est que la méthode Read peut ne pas retourner toutes les données en une fois. –

2

technique rapide et sale:

static byte[] StreamToByteArray(Stream inputStream) 
    { 
     if (!inputStream.CanRead) 
     { 
      throw new ArgumentException(); 
     } 

     // This is optional 
     if (inputStream.CanSeek) 
     { 
      inputStream.Seek(0, SeekOrigin.Begin); 
     } 

     byte[] output = new byte[inputStream.Length]; 
     int bytesRead = inputStream.Read(output, 0, output.Length); 
     Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length"); 
     return output; 
    } 

Test:

static void Main(string[] args) 
    { 
     byte[] data; 
     string path = @"C:\Windows\System32\notepad.exe"; 
     using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read)) 
     { 
      data = StreamToByteArray(fs); 
     } 

     Debug.Assert(data.Length > 0); 
     Debug.Assert(new FileInfo(path).Length == data.Length); 
    } 

Je demande, pourquoi voulez-vous lire un flux dans un octet [], si vous êtes désireux Pour copier le contenu d'un flux, puis-je suggérer d'utiliser MemoryStream et écrire votre flux d'entrée dans un flux de mémoire.

+3

Tous les flux ne prennent pas en charge la propriété Length –

+2

Il n'y a absolument aucune garantie que Read() renvoie tous les octets à lire. –

+1

Le message commence Rapide et sale. Dans certains cas triviaux, cela pourrait suffire. – Oybek

35

Dans .NET Framework 4 et versions ultérieures, la classe Stream dispose d'une méthode CopyTo intégrée que vous pouvez utiliser.

Pour les versions antérieures du cadre, la fonction d'aide pratique d'avoir est:

public static void CopyStream(Stream input, Stream output) 
{ 
    byte[] b = new byte[32768]; 
    int r; 
    while ((r = input.Read(b, 0, b.Length)) > 0) 
     output.Write(b, 0, r); 
} 

Ensuite, utilisez l'une des méthodes ci-dessus pour copier un MemoryStream et appelez GetBuffer sur elle:

var file = new FileStream("c:\\foo.txt", FileMode.Open); 

var mem = new MemoryStream(); 

// If using .NET 4 or later: 
file.CopyTo(mem); 

// Otherwise: 
CopyStream(file, mem); 

// getting the internal buffer (no additional copying) 
byte[] buffer = mem.GetBuffer(); 
long length = mem.Length; // the actual length of the data 
          // (the array may be longer) 

// if you need the array to be exactly as long as the data 
byte[] truncated = mem.ToArray(); // makes another copy 

Modifier: à l'origine, j'ai suggéré d'utiliser la réponse de Jason pour un Stream qui prend en charge la propriété Length. Mais il y avait un défaut car il supposait que le Stream retournerait tout son contenu dans un seul Read, ce qui n'est pas nécessairement vrai (pas pour un Socket, par exemple.) Je ne sais pas s'il y a un exemple d'implémentation Stream dans la BCL qui prend en charge Length, mais pourrait renvoyer les données dans des morceaux plus courts que vous demandez, mais comme tout le monde peut hériter Stream cela pourrait facilement être le cas.

Il est probablement plus simple pour la plupart des cas d'utiliser la solution générale ci-dessus, mais vous en supposant ne voulait lire directement dans un tableau qui est bigEnough:

byte[] b = new byte[bigEnough]; 
int r, offset; 
while ((r = input.Read(b, offset, b.Length - offset)) > 0) 
    offset += r; 

C'est, appeler à plusieurs reprises Read et déplacer la position que vous stockera les données à.

+0

Serait très intéressé par les raisons de downvotes! –

+0

@Earwicker: Je n'ai pas downvote parce que vous avez gentiment fait l'édition que j'ai demandée. OTOH, si je ne l'avais pas demandé, j'aurais baissé le nombre de "using" statements. J'ai effectivement décidé de ne pas mentionner cela dans ma demande, aussi! –

+0

Pourquoi se préoccuper de la mémoire lorsque vous pouvez simplement utiliser une liste et AddRange()? Il fait exactement la même chose sous le capot, autant que je sache. –

1

Vous pouvez également essayer de simplement lire dans les parties à la fois et l'élargissement du tableau d'octets retourné:

public byte[] StreamToByteArray(string fileName) 
{ 
    byte[] total_stream = new byte[0]; 
    using (Stream input = File.Open(fileName, FileMode.Open, FileAccess.Read)) 
    { 
     byte[] stream_array = new byte[0]; 
     // Setup whatever read size you want (small here for testing) 
     byte[] buffer = new byte[32];// * 1024]; 
     int read = 0; 

     while ((read = input.Read(buffer, 0, buffer.Length)) > 0) 
     { 
      stream_array = new byte[total_stream.Length + read]; 
      total_stream.CopyTo(stream_array, 0); 
      Array.Copy(buffer, 0, stream_array, total_stream.Length, read); 
      total_stream = stream_array; 
     } 
    } 
    return total_stream; 
} 
25
byte[] buf; // byte array 
    Stream stream=Page.Request.InputStream; //initialise new stream 
    buf = new byte[stream.Length]; //declare arraysize 
    stream.Read(buf, 0, buf.Length); // read from stream to byte array 
+1

Si je me souviens correctement "Lire" ne lit pas toujours toute la quantité disponible du flux - par exemple demander N octets, retourner M octets avec M

653

La solution la plus courte, je sais:

using(var memoryStream = new MemoryStream()) 
{ 
    sourceStream.CopyTo(memoryStream); 
    return memoryStream.ToArray(); 
} 
+63

Remarque: CopyTo n'est disponible qu'avec .NET Framework 4. –

+0

mais Est-ce que cela implique deux copies? – thalm

+6

Oui c'est le cas. Vous pouvez utiliser MemoryStream.GetBuffer() pour éviter la copie supplémentaire, mais prenez garde que la taille du tableau renvoyé n'est pas la taille des données. –

6

Ok, peut-être que je Je manque quelque chose ici, mais voici comment je le fais:

public static Byte[] ToByteArray(this Stream stream) { 
    Int32 length = stream.Length > Int32.MaxValue ? Int32.MaxValue : Convert.ToInt32(stream.Length); 
    Byte[] buffer = new Byte[length]; 
    stream.Read(buffer, 0, length); 
    return buffer; 
} 
+0

Pour cette méthode et la méthode de @ user734862 j'ai l'erreur suivante: 'Ce flux ne supporte pas les opérations de recherche' une exception System.NotSupportedException. Je pense que cela peut être dû au fait que je lis un fichier depuis un emplacement http et que je le renvoie ensuite. Il peut être différent lorsque vous travaillez avec un fichier sur votre système. –

+0

La méthode Stream.Read peut lire moins d'octets que ce que vous demandez. Vous devriez vérifier la valeur de retour de la méthode Read. – arkhivania

+0

Le flux renvoyé par, par exemple, Microsoft.SharePoint.Client.File.OpenBinaryDirect renvoie très souvent seulement 500 octets à la fois, quelle que soit la taille de votre tampon. Vous ne devriez jamais ignorer la valeur de retour de Stream.Read. –

2

si vous insérez un fichier à partir du périphérique mobile ou autre tableau

byte[] fileData = null; 
    using (var binaryReader = new BinaryReader(Request.Files[0].InputStream)) 
    { 
     fileData = binaryReader.ReadBytes(Request.Files[0].ContentLength); 
    } 
+0

Doit mentionner que vous pouvez réellement utiliser ceci sur n'importe quel FileStream. Dans WPF, vous ne pouvez pas utiliser 'Request.Files [0] .InputStream', mais vous pouvez faire' using (FileStream fs = new Fichier.OpenRead (nomfichier)) {var binaryReader = new BinaryReader (fs); fileData = binaryReader.ReadBytes ((int) fs.Length); } '. Merci pour le conseil! – vapcguy

12
Byte[] Content = new BinaryReader(file.InputStream).ReadBytes(file.ContentLength); 
+0

Je peux me tromper, mais cela semble être plus efficace que la méthode MemoryStream qui crée deux copies en mémoire. –

+0

Selon le scénario, votre exemple est très spécifique au flux de fichier dont vous pouvez déterminer la longueur. Qu'en est-il si l'entrée est un flux? comme Readbytes n'accepte que l'int32 – Vincent

0

« bigEnough » est un peu exagéré. Bien sûr, le tampon doit être "gros" mais la conception correcte d'une application doit inclure les transactions et les délimiteurs. Dans cette configuration, chaque transaction aurait une longueur prédéfinie, ainsi votre tableau anticiperait un certain nombre d'octets et l'insérerait dans un tampon de taille correcte. Les délimiteurs assureraient l'intégrité des transactions et seraient fournis dans chaque transaction. Pour rendre votre application encore meilleure, vous pouvez utiliser 2 canaux (2 sockets). On pourrait communiquer des transactions de messages de contrôle de longueur fixe qui comprendraient des informations sur la taille et le numéro de séquence de la transaction de données à transférer en utilisant le canal de données. Le récepteur accuserait réception de la mémoire tampon et seulement les données seraient envoyées. Si vous n'avez aucun contrôle sur l'expéditeur du flux, vous avez besoin d'un tableau multidimensionnel en tant que tampon. Les matrices de composants seraient suffisamment petites pour être gérables et assez grandes pour être pratiques en fonction de votre estimation des données attendues. La logique de processus recherche des délimiteurs de début connus, puis se termine par un délimiteur dans les tableaux d'éléments suivants. Une fois le délimiteur de fin trouvé, un nouveau tampon serait créé pour stocker les données pertinentes entre les délimiteurs et le tampon initial devrait être restructuré pour permettre l'élimination des données.

Dans la mesure où un code pour convertir un flux en tableau d'octets est un ci-dessous.

Stream s = yourStream; 
int streamEnd = Convert.ToInt32(s.Length); 
byte[] buffer = new byte[streamEnd]; 
s.Read(buffer, 0, streamEnd); 
13

J'utilise cette classe d'extension:

public static class StreamExtensions 
{ 
    public static byte[] ReadAllBytes(this Stream instream) 
    { 
     if (instream is MemoryStream) 
      return ((MemoryStream) instream).ToArray(); 

     using (var memoryStream = new MemoryStream()) 
     { 
      instream.CopyTo(memoryStream); 
      return memoryStream.ToArray(); 
     } 
    } 
} 

copie Juste la classe à votre solution et vous pouvez l'utiliser sur tous les flux:

byte[] bytes = myStream.ReadAllBytes() 

fonctionne très bien pour tous mes cours d'eau et économise beaucoup de code! Bien sûr, vous pouvez modifier cette méthode pour utiliser certaines des autres approches ici pour améliorer les performances si nécessaire, mais j'aime rester simple.