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

821

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

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

Stream s;
byte[] b;

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

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

  • 56
    Конечно, другой вопрос заключается в том, следует ли создавать байт [] из потока ... для больших данных предпочтительнее рассматривать поток как поток!
Теги:
inputstream
.net-3.5

16 ответов

1140
Лучший ответ

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

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

  • 1
    @Jon: если вы знаете точную длину вашего потока, можно ли использовать код, который написал автор?
  • 1
    @Pure: с помощью BinaryReader, да - он будет циклически повторяться до тех пор, пока поток не станет пустым или не будет прочитано достаточно данных. Не пытайтесь использовать Stream таким образом.
Показать ещё 22 комментария
621

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

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
  • 3
    Чем отличается ваш ответ от ответа Джона? Также я должен сделать это input.Position = 0, чтобы CopyTo работал.
  • 0
    @nathan, прочитайте файл из веб-клиента (filizesize = 1 МБ) - iis должен будет загрузить целые 1 МБ в свою память, верно?
Показать ещё 9 комментариев
89

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

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

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet accepted answer 
        return ReadFully(stream);
    }
}
  • 0
    Да, для чего все противники? Даже с самыми щедрыми предположениями, это работает только для потоков, которые уже являются MemoryStream s. Конечно, пример также явно неполон в том, как он использует неинициализированную переменную.
  • 3
    Это верно, спасибо за указание на это. Тем не менее, точка все еще стоит за MemoryStream, поэтому я исправил это, чтобы отразить это.
Показать ещё 2 комментария
60
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
  • 6
    MemoryStream должен быть создан с "new MemoryStream (file.PostedFile.ContentLength)", чтобы избежать фрагментации памяти.
46

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

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

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

  • 4
    Обратите внимание, что это не будет работать в .NET 3.5 и ниже, поскольку CopyTo не был доступен в Stream до 4.0.
9

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

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()
  • 55
    Я считаю плохой идеей помещать входной поток в блок использования. Эта ответственность должна лежать на процедуре вызова.
7

Я получаю ошибку времени компиляции с кодом Боба (то есть вопросника). 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);
}
5

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

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

Один из них в порядке... но вы столкнетесь с повреждением данных при отправке файлов через 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;
        }'
  • 0
    Я не вижу, где этот код позволяет избежать повреждения данных. Вы можете это объяснить?
  • 0
    Допустим, у вас есть картинка, и вы хотите отправить ее по SMTP. Вы, вероятно, будете использовать кодировку base64. По какой-то причине файл повреждается, если вы разбиваете его на байты. Однако использование двоичного считывателя позволит успешно отправить файл.
Показать ещё 2 комментария
2

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

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

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

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

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

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

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

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

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();
    }
}
-2
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();
            }
        }            
    }
  • 0
    Вы только что скопировали код из ответов № 1 и № 3, не добавляя ничего ценного. Пожалуйста, не делай этого. :)
  • 0
    При добавлении кода также кратко опишите предложенное решение.
-5

Мне удалось заставить его работать в одной строке:

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

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

  • 2
    Что если localStream не является MemoryStream ? Этот код не удастся.
  • 0
    localStream должен быть объектом на основе потока. подробнее об объекте на основе потока здесь stackoverflow.com/questions/8156896/…
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню