Domanda

Qual è il metodo preferito per creare un array di byte da un flusso di input?

Ecco la mia attuale soluzione con .NET 3.5.

Stream s;
byte[] b;

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

È ancora un'idea migliore leggere e scrivere blocchi dello stream?

È stato utile?

Soluzione

Dipende davvero se ci si può fidare o meno di s.Length . Per molti stream, semplicemente non sai quanti dati ci saranno. In questi casi, e prima di .NET 4, userei il codice in questo modo:

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

Con .NET 4 e versioni successive, utilizzerei < code> Stream.CopyTo , che equivale sostanzialmente al loop nel mio codice: crea il MemoryStream , chiama stream.CopyTo (ms) e quindi restituisce ms.ToArray () . Lavoro fatto.

Forse dovrei spiegare perché la mia risposta è più lunga delle altre. Stream.Read non garantisce che leggerà tutto ciò che viene richiesto. Se stai leggendo da un flusso di rete, ad esempio, potrebbe leggere il valore di un pacchetto e quindi restituirlo, anche se presto ci saranno più dati. BinaryReader.Read continuerà fino alla fine dello stream o alle dimensioni specificate, ma devi comunque conoscere le dimensioni con cui iniziare.

Il metodo sopra continuerà a leggere (e copiare in un MemoryStream ) fino a quando i dati non saranno esauriti. Chiede quindi al MemoryStream di restituire una copia dei dati in un array. Se conosci le dimensioni per iniziare - o pensa conosci le dimensioni, senza essere sicuro - puoi costruire il MemoryStream in modo che sia la dimensione per iniziare. Allo stesso modo puoi mettere un segno di spunta alla fine e se la lunghezza del flusso ha le stesse dimensioni del buffer (restituito da MemoryStream.GetBuffer ) quindi puoi semplicemente restituire il buffer. Quindi il codice sopra non è del tutto ottimizzato, ma almeno sarà corretto. Non si assume alcuna responsabilità per la chiusura dello stream - il chiamante dovrebbe farlo.

Vedi questo articolo per maggiori informazioni (e un'implementazione alternativa).

Altri suggerimenti

Mentre la risposta di Jon è corretta, sta riscrivendo il codice che esiste già in CopyTo . Quindi per .Net 4 usa la soluzione di Sandip, ma per la versione precedente di .Net usa la risposta di Jon. Il codice di Sandip sarebbe migliorato usando " usando " poiché le eccezioni in CopyTo sono, in molte situazioni, abbastanza probabili e lascerebbero MemoryStream non eliminato.

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

Voglio solo sottolineare che nel caso in cui possiedi un MemoryStream hai già memorystream.ToArray () per questo.

Inoltre, se hai a che fare con flussi di sottotipi sconosciuti o diversi e puoi ricevere un MemoryStream , puoi inoltrare questo metodo per quei casi e usare comunque la risposta accettata per gli altri, come in questo modo:

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();

solo i miei due centesimi ... la pratica che uso spesso è quella di organizzare i metodi come questo come un aiuto personalizzato

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

aggiungi lo spazio dei nomi al file di configurazione e usalo dove vuoi

Puoi persino renderlo più elaborato con le estensioni:

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

E quindi chiamalo come metodo normale:

byte[] arr = someStream.ToByteArray()

Puoi semplicemente usare il metodo ToArray () della classe MemoryStream, ad esempio

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

Ricevo un errore in fase di compilazione con il codice di Bob (ovvero il questionatore). Stream.Length è un lungo mentre BinaryReader.ReadBytes accetta un parametro intero. Nel mio caso, non mi aspetto di avere a che fare con stream abbastanza grandi da richiedere una lunga precisione, quindi utilizzo quanto segue:

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

Quello sopra è ok ... ma riscontrerai un danneggiamento dei dati quando invii materiale tramite SMTP (se necessario). Ho modificato qualcos'altro che aiuterà a inviare correttamente byte per 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;
        }'

Crea una classe helper e fai riferimento a essa ovunque tu voglia usarla.

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

Nel caso in cui piaccia a qualcuno, ecco una soluzione .NET 4+ formata come metodo di estensione senza la inutile chiamata Dispose su MemoryStream. Questa è un'ottimizzazione irrimediabilmente banale, ma vale la pena notare che non riuscire a smaltire un MemoryStream non è un vero fallimento.

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

Nello spazio dei nomi RestSharp.Extensions è presente il metodo ReadAsBytes. All'interno di questo metodo viene utilizzato MemoryStream e c'è lo stesso codice come in alcuni esempi in questa pagina ma quando si utilizza RestSharp questo è il modo più semplice.

using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();

Puoi usare questo metodo di estensione.

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

Questa è la funzione che sto usando, testata e ha funzionato bene. tenere presente che "input" non deve essere nullo e "input.position" deve reimpostare su "0" prima di leggere, altrimenti si interromperà il ciclo di lettura e nulla verrà letto per convertire in array.

    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;
        }
    }
public static byte[] ToByteArray(Stream stream)
    {
        if (stream is MemoryStream)
        {
            return ((MemoryStream)stream).ToArray();
        }
        else
        {
            byte[] buffer = new byte[16 * 1024];
            using (MemoryStream ms = new MemoryStream())
            {
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                return ms.ToArray();
            }
        }            
    }

Sono stato in grado di farlo funzionare su una sola riga:

byte [] byteArr= ((MemoryStream)localStream).ToArray();

come chiarito da johnnyRose , il codice sopra funziona solo per MemoryStream

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top