Question Création d'un tableau d'octets à partir d'un flux


Quelle est la méthode préférée pour créer un tableau d'octets à partir d'un flux d'entrée?

Voici ma solution actuelle avec .NET 3.5.

Stream s;
byte[] b;

using (BinaryReader br = new BinaryReader(s))
{
    b = br.ReadBytes((int)s.Length);
}

Est-ce encore une meilleure idée de lire et d'écrire des morceaux du flux?


725
2017-10-21 13:42


origine


Réponses:


Cela dépend vraiment si vous pouvez avoir confiance ou non s.Length. Pour de nombreux flux, vous ne savez pas combien de données il y aura. Dans de tels cas - et avant .NET 4 - j'utiliserais du code comme ceci:

public static byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

Avec .NET 4 et plus, j'utiliserais Stream.CopyTo, qui est fondamentalement équivalent à la boucle dans mon code - créer le MemoryStream, appel stream.CopyTo(ms) puis retour ms.ToArray(). Travail terminé.

Je devrais peut-être expliquer pourquoi ma réponse est plus longue que les autres. Stream.Read ne garantit pas qu'il lira tout ce qu'il demande. Si vous lisez un flux réseau, par exemple, il peut lire la valeur d'un paquet puis revenir, même s'il y aura bientôt plus de données. BinaryReader.Read va continuer jusqu'à la fin du flux ou la taille spécifiée, mais vous devez toujours connaître la taille pour commencer.

La méthode ci-dessus continuera à lire (et à copier dans un MemoryStream) jusqu'à épuisement des données. Il demande ensuite au MemoryStream de renvoyer une copie des données dans un tableau. Si vous connaissez la taille pour commencer - ou pense vous connaissez la taille, sans en être sûr - vous pouvez construire le MemoryStream avec cette taille pour commencer. De même, vous pouvez mettre une vérification à la fin, et si la longueur du flux est la même taille que le tampon (retourné par MemoryStream.GetBuffer) alors vous pouvez simplement retourner le tampon. Donc, le code ci-dessus n'est pas vraiment optimisé, mais sera au moins correct. Il n'assume aucune responsabilité pour la fermeture du flux - l'appelant devrait le faire.

Voir Cet article pour plus d'informations (et une implémentation alternative).


1064
2017-10-21 13:45



Bien que la réponse de Jon soit correcte, il réécrit du code qui existe déjà dans CopyTo. Donc, pour .Net 4, utilisez la solution de Sandip, mais pour la version précédente de .Net, utilisez la réponse de Jon. Le code de Sandip serait amélioré en utilisant "using" comme exceptions dans CopyTo sont, dans beaucoup de situations, très vraisemblables et laisseraient le MemoryStream non disposé.

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

585
2017-07-05 16:45



Je veux juste souligner que si vous avez un MemoryStream vous avez déjà memorystream.ToArray() pour ça.

En outre, si vous traitez avec des flux de sous-types inconnus ou différents et vous pouvez recevoir un MemoryStream, vous pouvez relayer sur ladite méthode pour ces cas et toujours utiliser la réponse acceptée pour les autres, comme ceci:

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}

88
2018-04-13 14:54



MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();

58
2018-02-12 13:33



juste mes deux cents cents ... la pratique que j'utilise souvent est d'organiser les méthodes comme celle-ci comme aide personnalisée

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

ajouter un espace de noms au fichier de configuration et l'utiliser où vous le souhaitez


45
2018-02-18 16:01



Vous pouvez même le rendre plus chic avec des extensions:

namespace Foo
{
    public static class Extensions
    {
        public static byte[] ToByteArray(this Stream stream)
        {
            using (stream)
            {
                using (MemoryStream memStream = new MemoryStream())
                {
                     stream.CopyTo(memStream);
                     return memStream.ToArray();
                }
            }
        }
    }
}

Et puis appelez-le comme une méthode régulière:

byte[] arr = someStream.ToByteArray()

9
2017-07-18 17:01



Je reçois une erreur de compilation avec le code de Bob (c'est-à-dire le questionneur). Stream.Length est long tandis que BinaryReader.ReadBytes prend un paramètre entier. Dans mon cas, je ne m'attends pas à avoir des Streams assez gros pour nécessiter une grande précision, donc j'utilise ce qui suit:

Stream s;
byte[] b;

if (s.Length > int.MaxValue) {
  throw new Exception("This stream is larger than the conversion algorithm can currently handle.");
}

using (var br = new BinaryReader(s)) {
  b = br.ReadBytes((int)s.Length);
}

8
2018-05-31 00:55



Celui ci-dessus est ok ... mais vous rencontrerez une corruption de données lorsque vous envoyez des choses sur SMTP (si vous en avez besoin). J'ai changé à quelque chose d'autre qui aidera à envoyer correctement octet pour byte: '

using System;
using System.IO;

        private static byte[] ReadFully(string input)
        {
            FileStream sourceFile = new FileStream(input, FileMode.Open); //Open streamer
            BinaryReader binReader = new BinaryReader(sourceFile);
            byte[] output = new byte[sourceFile.Length]; //create byte array of size file
            for (long i = 0; i < sourceFile.Length; i++)
                output[i] = binReader.ReadByte(); //read until done
            sourceFile.Close(); //dispose streamer
            binReader.Close(); //dispose reader
            return output;
        }'

4
2017-07-30 22:22



Vous pouvez simplement utiliser la méthode ToArray () de la classe MemoryStream, par exemple

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();

2
2017-07-11 19:21