it-roy-ru.com

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

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

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

Stream s;
byte[] b;

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

Это все-таки лучшая идея для чтения и записи фрагментов потока?

789
Bob

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

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

1153
Jon Skeet

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

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

Просто хочу указать, что если у вас есть 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);
    }
}
95
Fernando Neira
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
59
Sandip Patel

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

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

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

46
Mr. Pumpkin

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

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
Michal T

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

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

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

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

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

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

В случае, если кому-то это нравится, вот решение .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();
    }
}
1
SensorSmith

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

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

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

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

Это та функция, которую я использую, протестировал и работал хорошо . Имейте в виду, что «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;
        }
    }
0
Fred.S