Akıştan bayt dizisi oluşturma


913

Bir giriş akışından bir bayt dizisi oluşturmak için tercih edilen yöntem nedir?

İşte .NET 3.5 ile şu anki çözümüm.

Stream s;
byte[] b;

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

Akışın parçalarını okumak ve yazmak hala daha iyi bir fikir mi?


60
Tabii ki, başka bir soru, bir akıştan bir bayt [] oluşturmanız gerektiğidir ... büyük veriler için, akışı bir akış olarak ele almak tercih edilir!
Marc Gravell

2
Gerçekten de bayt [] yerine bir akış kullanmalısınız. Ancak akışları desteklemeyen bazı sistem API'ları vardır. Örneğin, bir akıştan X509Certificate2 oluşturamazsınız , ona bir bayt [] (veya bir dize) vermeniz gerekir. Bu durumda, x509 sertifikası büyük olasılıkla büyük veri olmadığından sorun yok .
1919'da

Yanıtlar:


1294

Gerçekten güvenip güvenemeyeceğinize bağlıdır s.Length. Birçok akış için, ne kadar veri olacağını bilmiyorsunuz. Bu gibi durumlarda - ve .NET 4'ten önce - böyle bir kod kullanırdım:

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

.NET 4 ve üstü ile, Stream.CopyTotemelde benim kod döngü - eşdeğer kullanın, oluşturmak MemoryStream, çağrı stream.CopyTo(ms)ve sonra dön ms.ToArray(). İş bitmiş.

Belki de cevabımın neden diğerlerinden daha uzun olduğunu açıklamalıyım. Stream.Readistenen her şeyi okuyacağını garanti etmez. Örneğin, bir ağ akışından okuyorsanız, bir paketin değerini okuyabilir ve yakında daha fazla veri olsa bile geri dönebilir. BinaryReader.Readakışın sonuna veya belirttiğiniz boyuta kadar devam eder, ancak yine de başlamak için boyutu bilmeniz gerekir.

Yukarıdaki yöntem MemoryStream, veri bitene kadar okumaya devam eder (ve a'ya kopyalar ). Daha sonra MemoryStream, bir dizideki verilerin bir kopyasını döndürmesini ister . Başlamak için boyutu biliyorsanız - veya boyutu bilmediğinizi düşünüyorsanız , emin olmadan - MemoryStreamile başlamak için bu boyutta olacak şekilde yapılandırabilirsiniz . Benzer şekilde, sonunda bir kontrol koyabilirsiniz ve akışın uzunluğu arabellekle (döndürülen MemoryStream.GetBuffer) ile aynı boyuttaysa, ara belleği geri verebilirsiniz. Dolayısıyla yukarıdaki kod oldukça optimize edilmemiştir, ancak en azından doğru olacaktır. Akışı kapatmak için herhangi bir sorumluluk kabul etmez - arayan bunu yapmalıdır.

Daha fazla bilgi (ve alternatif bir uygulama) için bu makaleye bakın .


9
@Jon, bahsetmeye değer olabilir yoda.arachsys.com/csharp/readbinary.html
Sam Saffron

6
@Jeff: Burada gerçekten bağlamımız yok, ancak bir akışa yazıyorsanız, evet ise okumadan önce "geri sarmanız" gerekir. Akış içinde nerede olduğunuzu söyleyen sadece bir "imleç" vardır - okumak için değil, yazmak için ayrı.
Jon Skeet

5
@ Jeff: Arayanın sorumluluğu. Sonuçta, akış aranmayabilir (örneğin bir ağ akışı) veya basitçe geri sarmaya gerek olmayabilir.
Jon Skeet

18
Neden olduğunu 16*1024özellikle sorabilir miyim ?
Anyname Donotcare

5
@just_name: Bunun herhangi bir önemi olup olmadığını bilmiyorum, ancak (16 * 1024) Int16.MaxValue'nun yarısı oluyor :)
caesay

734

Jon'un cevabı doğru olsa da, zaten mevcut olan kodu yeniden yazıyor CopyTo. Net 4 için Sandip'in çözümünü kullanın, ancak .Net'in önceki sürümü için Jon'un yanıtını kullanın. Sandip'in kodu CopyTo, birçok durumda istisna olarak "kullanmanın" kullanılmasıyla geliştirilecektir ve çoğu durumda MemoryStreamimha edilmeyecektir.

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

6
Cevabınız ile Jon'un arasındaki fark nedir? Ayrıca CopyTo çalışması için bu input.Position = 0 yapmalıyım.
Jeff

1
@nathan, web istemcisinden bir dosya oku (filizesize = 1mb) - iis tüm 1mb'yi belleğine yüklemesi gerekecek değil mi?
Royi Namir

5
@Jeff, cevabım sadece .Net 4 veya üstü üzerinde çalışacak, Jons daha sonraki sürümlerde bize sağlanan işlevselliği yeniden yazarak daha düşük sürümlerde çalışacak. CopyTo'nun yalnızca geçerli konumdan kopyalayacağını, doğru bir akışınız varsa ve baştan kopyalamak istiyorsanız, kodunuzu veya girişinizi kullanarak başlangıca geçebilirsiniz. ancak birçok durumda akışınız Aramayabilir.
Nathan Phillips

5
inputzaten MemorySteamkısa devre olup olmadığını kontrol etmeye değer olabilir . Arayanın pas geçmenin aptalca olacağını biliyorum MemoryStreamama ...
Jodrell

3
@Jodrell, aynen öyle. Milyonlarca küçük akışı belleğe kopyalıyorsanız ve bunlardan biri MemoryStream, optimizasyonun bağlamınızda mantıklı olup olmadığı, milyonlarca tip dönüşümü yapmak için harcanan sürenin MemoryStream, başka MemoryStream.
Nathan Phillips

114

Sadece bunun memorystream.ToArray()için zaten bir MemoryStream'iniz varsa belirtmek istersiniz .

Ayrıca, bilinmeyen veya farklı alt türlerin akışlarıyla uğraşıyorsanız ve bir alabilirseniz MemoryStream, bu durumlar için adı geçen yöntemi aktarabilir ve yine de diğerleri için kabul edilen cevabı kullanabilirsiniz:

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

1
Ha, tüm upvotes ne için? En cömert varsayımlarla bile, bu sadece zaten olan akışlar için çalışır MemoryStream. Tabii ki örnek de, başlatılmamış bir değişkeni nasıl kullandığına dair açıkça eksik.
Roman Starkov

3
Bu doğru, bunu belirttiğin için teşekkürler. Nokta yine de MemoryStream için duruyor, bu yüzden bunu yansıtmak için sabitledim.
Fernando Neira

MemoryStream için başka bir olasılığın MemoryStream.GetBuffer () olduğunu da belirtmek gerekir, ancak bazı gotcha'lar da vardır. Bkz. Stackoverflow.com/questions/1646193/… ve krishnabhargav.blogspot.dk/2009/06/…
RenniePet

4
Bu aslında Skeet koduna bir hata getiriyor; Arama stream.Seek(1L, SeekOrigin.Begin)yaparsanız, kolayca çağırmadan önce, akış bir bellek akışı ise, başka bir akıştan daha fazla 1 bayt alırsınız. Arayan, geçerli konumun akışın sonuna kadar okumayı beklerse CopyToveya ToArray(); Çoğu durumda bu bir sorun olmayacaktır, ancak arayan bu ilginç davranışı bilmiyorsa kafası karışacaktır.
derli toplu

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

9
Bellek parçalanmasını önlemek için MemoryStream "yeni MemoryStream (file.PostedFile.ContentLength)" ile oluşturulmalıdır.
Dan Randolph

52

sadece çift sentim ... sık kullandığım uygulama, böyle bir yöntemi özel bir yardımcı olarak organize etmektir

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

yapılandırma dosyasına ad alanı ekleyin ve istediğiniz yere kullanın


5
4.0'a kadar CopyTokullanılabilir olmadığından, bunun .NET 3.5 ve daha önceki sürümlerde çalışmayacağını unutmayın Stream.
Tim

16

MemoryStream sınıfının ToArray () yöntemini ex-

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

10

Hatta uzantılarla daha da meraklı olabilirsiniz:

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

Ve sonra düzenli bir yöntem olarak adlandırın:

byte[] arr = someStream.ToByteArray()

67
Giriş akışını bir kullanım bloğuna koymak kötü bir fikir olduğunu düşünüyorum. Bu sorumluluk çağıran prosedürle ilgili olmalıdır.
Jeff

7

Bob (yani soru soran) kodu ile derleme zamanı hatası alıyorum. Stream.Length uzun, BinaryReader.ReadBytes bir tamsayı parametresi alır. Benim durumumda, uzun hassasiyet gerektirecek kadar büyük Akışlarla uğraşmayı beklemiyorum, bu yüzden aşağıdakileri kullanıyorum:

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

5

Herkes beğenirse, burada MemoryStream üzerinde gereksiz Dispose çağrısı olmadan bir uzantı yöntemi olarak oluşturulan bir .NET 4+ sadece çözümü. Bu umutsuzca önemsiz bir optimizasyon, ancak bir MemoryStream öğesinin imha edilmemesinin gerçek bir hata olmadığını belirtmek gerekir.

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

3

Yukarıdaki bir ok ... ama SMTP üzerinden bir şeyler gönderdiğinizde (gerekirse) veri bozulması ile karşılaşacaksınız. Baytın bayt için doğru bir şekilde gönderilmesine yardımcı olacak başka bir şeye değiştirdim: '

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

Bu kodun veri bozulmasını önlediği yeri görmüyorum. Bunu açıklayabilir misin?
Nippey

Diyelim ki bir resminiz var ve onu SMTP ile göndermek istiyorsunuz. Muhtemelen base64 kodlaması kullanacaksınız. Herhangi bir nedenle, bayt olarak böldüğünüzde dosya bozulur. Ancak, bir ikili okuyucu kullanmak dosyanın başarıyla gönderilmesine izin verecektir.
NothinRandom

3
Biraz eski, ama ben bu ayılar söz hissettim - @NothinRandom uygulama akışları değil, dizeleri ile çalışır sağlar. Bu durumda sadece File.ReadAllBytes'i kullanmak en basit yöntem olabilir.
XwipeoutX

1
Tehlikeli kod stili nedeniyle aşağı oylama (otomatik Atma / kullanma yok).
arni

Ne yazık ki sadece -1, soru ile ilgisi yok, girdi adı dosya adı parametresi, bertaraf değil, okuma tamponu, filemode ve ikili okuyucu bayt bayt bayt okumak neden?
Aridane Álamo

2

Bir yardımcı sınıf oluşturun ve kullanmak istediğiniz yere referans verin.

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

2

RestSharp.Extensions ad alanında ReadAsBytes yöntemi vardır. Bu yöntemin içinde MemoryStream kullanılır ve bu sayfadaki bazı örneklerde olduğu gibi aynı kod vardır, ancak RestSharp kullanırken bu en kolay yoldur.

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

1

Bu uzantı yöntemini kullanabilirsiniz.

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

1

Bu, kullandığım, test ettiğim ve iyi çalıştığım işlev. lütfen 'input' değerinin boş olmaması ve 'input.position' ifadesinin okumadan önce '0' değerine ayarlanması gerektiğini unutmayın; aksi takdirde okuma döngüsünü kıracaktır ve diziye dönüştürmek için hiçbir şey okunmayacaktır.

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

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

Kodu # 1 ve # 3 yanıtından değerli bir şey eklemeden kopyaladınız. Lütfen yapma. :)
CodeCaster

Bir kod eklediğinizde, önerilen çözümü kısaca açıklayın.
yakobom

-5

i tek bir satır üzerinde çalışmasını başardı:

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

johnnyRose tarafından açıklandığı gibi , yukarıdaki kod sadece MemoryStream için çalışacaktır


2
Ya localStreambir değil MemoryStream? Bu kod başarısız olur.
johnnyRose

localStream, akış tabanlı bir nesne olmalıdır. Burada akış tabanlı nesne hakkında daha fazla bilgi stackoverflow.com/questions/8156896/…
Abba

1
Benim önerim çalışıyordum Eğer döküm çalışırsanız, olan localStreambir etmek MemoryStream, ancak localStreambir değil bir MemoryStream, bu olacak başarısız. Bu kod iyi derlenecektir, ancak gerçek türüne bağlı olarak çalışma zamanında başarısız olabilir localStream. Bir taban türünü her zaman alt türüne rastgele atayamazsınız; daha fazlasını buradan okuyun . Bu, neden her zaman bunu yapamayacağınızı açıklayan başka bir iyi örnektir .
johnnyRose

Yukarıdaki yorumumu ayrıntılı olarak açıklamak için: Tüm MemoryStreams Akışlar, ancak tüm Akışlar MemoryStreams değildir.
johnnyRose

Akış tabanlı tüm nesnelerde temel tür olarak Akış bulunur. Stream'in kendisi her zaman bellek akışına dönüştürülebilir. Meomry Stream'e hangi akış tabanlı nesneyi yayınlamaya çalışırsanız çalışın, her zaman çalışmalıdır. Buradaki amacımız, akış nesnesini bayt dizisine dönüştürmektir. Bana başarısız olacağına dair bir dava verebilir misiniz?
Abba
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.