2008-10-21 7 views
676

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

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

Stream s; 
byte[] b; 

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

Неужели по-прежнему лучше читать и писать куски потока?

+49

Конечно, другой вопрос: * должен * создать байт [] из потока ... для больших данных предпочтительнее обрабатывать поток, как, ну, поток! – 2008-10-21 13:57:27

ответ

992

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

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

+1

@Jon: если вы знаете точную длину своего потока, можно ли использовать код, который написал постер? – 2008-11-10 12:43:06

+1

@Pure: с помощью BinaryReader да - цикл будет циклическим до тех пор, пока поток не станет пустым или данные будут прочитаны. Однако не пытайтесь использовать Stream. – 2008-11-10 12:44:25

+8

@ Jon, может быть, стоит упомянуть http://www.yoda.arachsys.com/csharp/readbinary.html – 2009-02-12 23:11:33

77

Просто хочу указать, что если у вас есть 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); 
    } 
} 
+0

Да, для чего все это стоит? Даже с самыми щедрыми предположениями это работает только для потоков, которые уже являются «MemoryStream`s». Конечно, пример также явно неполный, в том, как он использует неинициализированную переменную. – 2010-07-30 23:48:41

+3

Правильно, спасибо, что указали это. Тем не менее, точка по-прежнему относится к MemoryStream, поэтому я исправил ее, чтобы отразить это. – 2010-10-06 10:21:28

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

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

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

Хотя ответ Джона является правильным, он переписыванием коды, который уже существует в CopyTo. Поэтому для .Net 4 используйте решение Sandip, но для предыдущей версии .Net используйте ответ Джона. Код 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. – Jeff 2012-03-20 00:42:50

8

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

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

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

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

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

-2

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

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

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

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