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 toujours une meilleure idée de lire et d’écrire des morceaux du flux?
Tout dépend si vous pouvez ou non faire confiance à s.Length
. Pour de nombreux flux, vous ne savez simplement pas combien de données il y aura. Dans de tels cas, et avant .NET 4, j'utiliserais un code comme celui-ci:
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 les versions ultérieures, je voudrais utiliser Stream.CopyTo
, qui est fondamentalement équivalent à la boucle de mon code - créez la MemoryStream
, appelez stream.CopyTo(ms)
puis renvoyez ms.ToArray()
. Travail accompli.
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 de réseau, par exemple, il peut lire un paquet, puis le renvoyer, même s'il y aura bientôt plus de données. BinaryReader.Read
continuera 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 une MemoryStream
) jusqu'à ce qu'il n'y ait plus de données. Il demande ensuite à la MemoryStream
de renvoyer une copie des données d'un tableau. Si vous connaissez la taille avec laquelle commencer - ou pensez, vous en connaissez la taille, sans être sûr -, vous pouvez construire la MemoryStream
pour qu'elle corresponde à cette taille. De même, vous pouvez cocher la fin et si la longueur du flux est la même que celle du tampon (renvoyée par MemoryStream.GetBuffer
), vous pouvez simplement renvoyer le tampon. Donc, le code ci-dessus n'est pas tout à fait optimisé, mais sera au moins correct. Il n'assume aucune responsabilité quant à la fermeture du flux - l'appelant doit le faire.
Voir cet article pour plus d'informations (et une implémentation alternative).
Bien que la réponse de Jon soit correcte, il réécrit le 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 "utiliser" comme exceptions dans CopyTo
sont, dans de nombreuses situations, assez probables et laisserait la MemoryStream
non éliminée.
public static byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
Je veux juste souligner que si vous avez un MemoryStream, vous avez déjà memorystream.ToArray()
pour cela.
De même, si vous traitez avec des flux de sous-types inconnus ou différents et que vous pouvez recevoir un MemoryStream
, vous pouvez utiliser cette méthode pour vous relayer et 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);
}
}
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
juste quelques centimes… la pratique que j'utilise souvent est d'organiser des méthodes comme celle-ci en tant qu'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();
}
}
}
ajoutez un espace de noms au fichier de configuration et utilisez-le où vous le souhaitez
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 appelez-le comme une méthode régulière:
byte[] arr = someStream.ToByteArray()
Vous pouvez simplement utiliser la méthode ToArray () de la classe MemoryStream, par exemple
MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
Je reçois une erreur de compilation avec le code de Bob (le questionneur). Stream.Length est un long tandis que BinaryReader.ReadBytes prend un paramètre entier. Dans mon cas, je ne m'attends pas à traiter avec des flux suffisamment volumineux pour nécessiter une longue précision. J'utilise donc les éléments suivants:
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);
}
Celui ci-dessus est ok ... mais vous rencontrerez une corruption de données lorsque vous enverrez des fichiers via SMTP (si vous en avez besoin). Je suis passé à autre chose qui aidera à envoyer correctement octet par octet: '
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;
}'
Créez une classe d'assistance et référencez-la où vous souhaitez l'utiliser.
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
}
Si cela vous convient, voici une solution .NET 4+ uniquement conçue comme une méthode d’extension sans l’appel inutile de Dispose sur le MemoryStream. Il s'agit d'une optimisation désespérément triviale, mais il est à noter que le fait de ne pas disposer d'un MemoryStream n'est pas un véritable échec.
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
var ms = new MemoryStream();
input.CopyTo(ms);
return ms.ToArray();
}
}
Vous pouvez utiliser cette méthode d'extension.
public static class StreamExtensions
{
public static byte[] ToByteArray(this Stream stream)
{
var bytes = new List<byte>();
int b;
while ((b = stream.ReadByte()) != -1)
bytes.Add((byte)b);
return bytes.ToArray();
}
}
Dans l'espace de noms RestSharp.Extensions, il y a la méthode ReadAsBytes. MemoryStream est utilisé à l'intérieur de cette méthode et le code est identique à celui de certains exemples de cette page, mais lorsque vous utilisez RestSharp, c'est la méthode la plus simple.
using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();
C’est la fonction que j’utilise, teste et fonctionne bien . Veuillez garder à l’esprit que 'input' ne doit pas être nul et que 'input.position' doit être réinitialisé à '0' avant de lire, sinon cela cassera la boucle de lecture. et rien ne lira pour convertir en tableau.
public static byte[] StreamToByteArray(Stream input)
{
if (input == null)
return null;
byte[] buffer = new byte[16 * 1024];
input.Position = 0;
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
byte[] temp = ms.ToArray();
return temp;
}
}