Создание байтового массива из потока - PullRequest
828 голосов
/ 21 октября 2008

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

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

Stream s;
byte[] b;

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

Лучше ли читать и записывать куски потока?

Ответы [ 17 ]

1191 голосов
/ 21 октября 2008

Это действительно зависит от того, можете ли вы доверять 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), то вы можете просто вернуть буфер. Таким образом, приведенный выше код не совсем оптимизирован, но, по крайней мере, будет правильным. Он не несет никакой ответственности за закрытие потока - вызывающий должен сделать это.

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

655 голосов
/ 05 июля 2011

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

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
97 голосов
/ 13 апреля 2010

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

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

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}
61 голосов
/ 12 февраля 2011
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
48 голосов
/ 18 февраля 2013

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

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

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

10 голосов
/ 18 июля 2012

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

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()
8 голосов
/ 11 июля 2018

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

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
7 голосов
/ 31 мая 2011

Я получаю ошибку во время компиляции с кодом Боба (т.е. спрашивающего). 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);
}
3 голосов
/ 31 июля 2012

С приведенным выше кодом все в порядке ... но вы столкнетесь с повреждением данных при отправке материалов по 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;
        }'
2 голосов
/ 20 сентября 2018

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

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