Создание байтового массива из потока

Каков предпочтительный метод для создания байтового массива из входного потока?

Вот мое текущее решение с .NET 3.5.

Stream s;
byte[] b;

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

Это все еще лучшая идея, чтобы читать и писать куски потока?

21.10.2008 13:42:55
Конечно, другой вопрос заключается в том, следует ли создавать байт [] из потока ... для больших данных предпочтительно рассматривать поток как поток!
Marc Gravell♦ 21.10.2008 13:57:27
В самом деле, вы, вероятно, должны использовать поток вместо байта []. Но есть некоторые системные API, которые не поддерживают потоки. Например, вы не можете создать X509Certificate2 из потока, вы должны дать ему байт [] (или строку). В этом случае это нормально, поскольку сертификат x509, вероятно, не большие данные .
0xced 17.05.2019 08:19:08
16 ОТВЕТОВ
РЕШЕНИЕ

Это действительно зависит от того, можете ли вы доверять или нет s.Length. Для многих потоков вы просто не знаете, сколько будет данных. В таких случаях - и до .NET 4 - я бы использовал такой код:

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 и выше, я бы использовал Stream.CopyTo, что в основном эквивалентно циклу в моем коде - создать MemoryStream, вызвать stream.CopyTo(ms)и затем вернуться ms.ToArray(). Дело сделано.

Возможно, мне следует объяснить, почему мой ответ длиннее других. Stream.Readне гарантирует, что он прочитает все, о чем просил. Например, если вы читаете из сетевого потока, он может прочитать стоимость одного пакета и затем вернуться, даже если скоро будет больше данных. BinaryReader.Readбудет продолжаться до конца потока или указанного вами размера, но вы все равно должны знать размер для начала.

Вышеуказанный метод будет продолжать читать (и копировать в MemoryStream), пока не закончатся данные. Затем он просит MemoryStreamвернуть копию данных в массиве. Если вы знаете размер, с которого начинаете - или думаете, что знаете размер, не будучи уверенным - вы можете с самого начала MemoryStreamсоздать такой размер. Аналогично вы можете поставить проверку в конце, и если длина потока равна размеру буфера (возвращаемого MemoryStream.GetBuffer), то вы можете просто вернуть буфер. Таким образом, приведенный выше код не совсем оптимизирован, но, по крайней мере, будет правильным. Он не несет никакой ответственности за закрытие потока - вызывающий должен сделать это.

Смотрите эту статью для получения дополнительной информации (и альтернативной реализации).

1282
6.06.2019 12:36:37
@Jon, возможно, стоит упомянуть yoda.arachsys.com/csharp/readbinary.html
Sam Saffron 12.02.2009 23:11:33
@Jeff: у нас на самом деле нет контекста, но если вы писали в поток, то да, вам нужно «перемотать» его перед чтением. Есть только один «курсор», говорящий о том, где вы находитесь в потоке - не один для чтения, а отдельный для записи.
Jon Skeet 20.03.2012 06:42:50
@Jeff: это ответственность звонящего. В конце концов, поток может быть недоступен для поиска (например, сетевой поток), или может просто не быть необходимости перематывать его.
Jon Skeet 21.03.2012 10:19:27
Могу я спросить, почему 16*1024конкретно?
Anyname Donotcare 12.04.2012 10:30:50
@just_name: я не знаю, имеет ли это какое-либо значение, но (16 * 1024) оказывается половиной Int16.MaxValue :)
caesay 16.04.2012 02:18:19

Просто хочу указать, что в случае, если у вас есть MemoryStream, у вас уже есть memorystream.ToArray()для этого.

Кроме того, если вы имеете дело с потоками неизвестных или разных подтипов и можете получить a MemoryStream, вы можете ретранслировать указанный метод для этих случаев и по-прежнему использовать принятый ответ для других, например:

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}
113
25.09.2016 15:34:05
Да, для чего все противники? Даже при самых щедрых допущениях это работает только для потоков, которые уже MemoryStreams. Конечно, пример также явно неполон в том, как он использует неинициализированную переменную.
Roman Starkov 30.07.2010 23:48:41
Это верно, спасибо за указание на это. Точка все еще стоит за MemoryStream, хотя, поэтому я исправил это, чтобы отразить это.
Fernando Neira 6.10.2010 10:21:28
Просто отметим, что для MemoryStream есть еще одна возможность - MemoryStream.GetBuffer (), хотя здесь есть некоторые ошибки. См stackoverflow.com/questions/1646193/... и krishnabhargav.blogspot.dk/2009/06/...
RenniePet 2.01.2013 05:29:01
Это фактически вносит ошибку в код Skeet; Если вы вызываете stream.Seek(1L, SeekOrigin.Begin), прежде чем вызывать с готовностью, если поток является потоком памяти, вы получите на 1 байт больше, чем если бы это был любой другой поток. Если вызывающая сторона ожидает чтения с того места, где находится текущая позиция, до конца потока, то вы не должны использовать CopyToили ToArray(); В большинстве случаев это не будет проблемой, но если вызывающий не знает об этом странном поведении, он будет сбит с толку.
leat 7.08.2015 08:26:38
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
67
12.02.2011 13:33:29
MemoryStream следует создавать с помощью «new MemoryStream (file.PostedFile.ContentLength)», чтобы избежать фрагментации памяти.
Dan Randolph 20.06.2016 20:04:36

Я получаю ошибку во время компиляции с кодом Боба (т.е. спрашивающего). Stream.Length является длинным, тогда как BinaryReader.ReadBytes принимает целочисленный параметр. В моем случае я не ожидаю иметь дело с потоками, достаточно большими, чтобы требовать высокой точности, поэтому я использую следующее:

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);
}
7
31.05.2011 00:55:31

Хотя ответ Джона верен, он переписывает код, который уже существует в CopyTo. Так что для .Net 4 используйте решение Sandip, но для предыдущей версии .Net используйте ответ Джона. Код Сандипа будет улучшен путем использования «использования» в качестве исключений, CopyToкоторые во многих ситуациях вполне вероятны и оставят MemoryStreamнеиспользуемыми.

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
722
24.01.2019 13:34:10
Чем отличается ваш ответ от ответа Джона? Также я должен сделать это input.Position = 0, чтобы CopyTo работал.
Jeff 20.03.2012 00:42:50
@nathan, прочитайте файл из веб-клиента (filizesize = 1mb) - iis должен будет загрузить целые 1mb в свою память, верно?
Royi Namir 13.11.2012 08:14:56
@Jeff, мой ответ будет работать только на .Net 4 или выше, Jons будет работать на более низких версиях, переписав функциональность, предоставленную нам в более поздней версии. Вы правы, что CopyTo будет копировать только с текущей позиции, если у вас есть поток Seekable и вы хотите копировать с начала, то вы можете перейти к началу, используя свой код или input.Seek (0, SeekOrigin.Begin), хотя во многих случаях ваш поток может быть недоступен для поиска.
Nathan Phillips 26.02.2013 16:20:46
возможно, стоит проверить, если inputэто уже MemorySteamи короткое замыкание. Я знаю, что было бы глупо, чтобы вызывающий игрок прошел, MemoryStreamно ...
Jodrell 27.03.2013 12:19:59
@Jodrell, именно так. Если вы копируете миллионы небольших потоков в память, и один из них - это MemoryStreamто, имеет ли смысл оптимизация в вашем контексте, это сравнение времени, затраченного на миллионы преобразований типов, со временем, затрачиваемым на копирование того, который находится MemoryStreamв другой MemoryStream.
Nathan Phillips 22.09.2015 14:47:01

Вы даже можете сделать его более привлекательным с помощью расширений:

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

А затем вызовите его как обычный метод:

byte[] arr = someStream.ToByteArray()
11
19.07.2012 13:05:52
Я считаю плохой идеей помещать входной поток в блок использования. Эта ответственность должна лежать на процедуре вызова.
Jeff 22.08.2013 19:33:05

С приведенным выше кодом все в порядке ... но вы столкнетесь с повреждением данных при отправке материалов по SMTP (если вам это нужно). Я изменил что-то еще, что поможет правильно отправить байт за байтом: '

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
30.07.2012 22:22:24
Я не вижу, где этот код позволяет избежать повреждения данных. Вы можете это объяснить?
Nippey 11.10.2012 07:38:27
Допустим, у вас есть изображение, и вы хотите отправить его по SMTP. Вы, вероятно, будете использовать кодировку base64. По какой-то причине файл повреждается, если вы разбиваете его на байты. Однако использование двоичного считывателя позволит успешно отправить файл.
NothinRandom 29.10.2012 23:36:42
Несколько староват, но я чувствовал, что стоит упомянуть - реализация @NothinRandom обеспечивает работу со строками, а не с потоками. Однако в этом случае, вероятно, было бы проще всего использовать File.ReadAllBytes.
XwipeoutX 11.04.2014 06:35:01
Downvote из-за опасного стиля кода (без автоматического удаления / использования).
arni 1.12.2017 17:10:31

только мои пара центов ... практика, которую я часто использую, состоит в том, чтобы организовать методы как этот как пользовательский помощник

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

добавить пространство имен в файл конфигурации и использовать его где угодно

52
18.02.2013 16:01:22
Обратите внимание, что это не будет работать в .NET 3.5 и ниже, так как CopyToне было доступно Streamдо 4.0.
Tim 14.10.2014 18:31:13

я смог заставить его работать в одной строке:

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

как пояснил johnnyRose , вышеприведенный код будет работать только для MemoryStream

-5
23.05.2017 11:33:24
Что делать, если localStreamне MemoryStream? Этот код не удастся.
johnnyRose 17.03.2017 15:35:49
localStream должен быть объектом на основе потока. подробнее об объекте на основе потоков здесь stackoverflow.com/questions/8156896/…
Abba 17.03.2017 20:48:04
То , что я пытался предложить это, если вы пытаетесь гипс localStreamк MemoryStream, но localStreamэто неMemoryStream , он будет не в состоянии . Этот код прекрасно скомпилируется, но может не работать во время выполнения, в зависимости от фактического типа localStream. Вы не всегда можете произвольно привести базовый тип к дочернему типу; читайте больше здесь . Это еще один хороший пример, который объясняет, почему вы не всегда можете сделать это.
johnnyRose 17.03.2017 21:01:19
Чтобы уточнить мой комментарий выше: все MemoryStreams являются Streams, но не все Streams являются MemoryStreams.
johnnyRose 17.03.2017 21:05:55
все объекты на основе потока имеют поток в качестве базового типа. И сам поток всегда может быть преобразован в поток памяти. Независимо от того, какой объект на основе потока вы пытаетесь преобразовать в Meomry Stream, он всегда должен работать. Наша цель здесь - преобразовать объект потока в массив байтов. Можете ли вы дать мне случай, когда он потерпит неудачу?
Abba 20.03.2017 17:38:12
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();
            }
        }            
    }
-1
17.05.2017 13:42:28
Вы только что скопировали код из ответов № 1 и № 3, не добавляя ничего ценного. Пожалуйста, не делай этого. :)
CodeCaster 17.05.2017 13:51:59
При добавлении кода также кратко опишите предложенное решение.
yakobom 17.05.2017 14:09:13

Вы можете использовать этот метод расширения.

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
27.07.2017 06:49:07

Вы можете просто использовать метод ToArray () класса MemoryStream, например,

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
14
24.01.2019 19:39:01

Создайте вспомогательный класс и ссылайтесь на него везде, где вы хотите его использовать.

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

В случае, если кому-то это нравится, вот решение .NET 4+, созданное как метод расширения без ненужного вызова Dispose для MemoryStream. Это безнадежно тривиальная оптимизация, но стоит отметить, что неудача в утилизации MemoryStream не является реальной ошибкой.

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

Это функция, которую я использую, протестировал и хорошо работал. Пожалуйста, имейте в виду, что «input» не должен быть нулевым, а «input.position» должен сбрасываться в «0» перед чтением, иначе это нарушит цикл чтения и ничего не будет прочитано для преобразования в массив.

    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
10.01.2019 02:47:26

В пространстве имен RestSharp.Extensions есть метод ReadAsBytes. Внутри этого метода используется MemoryStream, и на этой странице есть тот же код, что и в некоторых примерах, но когда вы используете RestSharp, это самый простой способ.

using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();
2
14.01.2019 15:31:19