我如何将一个流转换成一个字节[]在c# ?

在c#中是否有简单的方法或方法将Stream转换为byte[] ?

600863 次浏览

像这样调用下一个函数

byte[] m_Bytes = StreamHelper.ReadToEnd (mystream);

功能:

public static byte[] ReadToEnd(System.IO.Stream stream)
{
long originalPosition = 0;


if(stream.CanSeek)
{
originalPosition = stream.Position;
stream.Position = 0;
}


try
{
byte[] readBuffer = new byte[4096];


int totalBytesRead = 0;
int bytesRead;


while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
{
totalBytesRead += bytesRead;


if (totalBytesRead == readBuffer.Length)
{
int nextByte = stream.ReadByte();
if (nextByte != -1)
{
byte[] temp = new byte[readBuffer.Length * 2];
Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
readBuffer = temp;
totalBytesRead++;
}
}
}


byte[] buffer = readBuffer;
if (readBuffer.Length != totalBytesRead)
{
buffer = new byte[totalBytesRead];
Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
}
return buffer;
}
finally
{
if(stream.CanSeek)
{
stream.Position = originalPosition;
}
}
}
Stream s;
int len = (int)s.Length;
byte[] b = new byte[len];
int pos = 0;
while((r = s.Read(b, pos, len - pos)) > 0) {
pos += r;
}

需要稍微复杂一点的解决方案是s.Length超过Int32.MaxValue。但是如果需要将这么大的流读入内存,则可能需要考虑另一种解决问题的方法。

编辑:如果你的流不支持Length属性,使用Earwicker的解决方案进行修改。

public static class StreamExtensions {
// Credit to Earwicker
public static void CopyStream(this Stream input, Stream output) {
byte[] b = new byte[32768];
int r;
while ((r = input.Read(b, 0, b.Length)) > 0) {
output.Write(b, 0, r);
}
}
}


[...]


Stream s;
MemoryStream ms = new MemoryStream();
s.CopyStream(ms);
byte[] b = ms.GetBuffer();

快速和肮脏的技巧:

    static byte[] StreamToByteArray(Stream inputStream)
{
if (!inputStream.CanRead)
{
throw new ArgumentException();
}


// This is optional
if (inputStream.CanSeek)
{
inputStream.Seek(0, SeekOrigin.Begin);
}


byte[] output = new byte[inputStream.Length];
int bytesRead = inputStream.Read(output, 0, output.Length);
Debug.Assert(bytesRead == output.Length, "Bytes read from stream matches stream length");
return output;
}

测试:

    static void Main(string[] args)
{
byte[] data;
string path = @"C:\Windows\System32\notepad.exe";
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
{
data = StreamToByteArray(fs);
}


Debug.Assert(data.Length > 0);
Debug.Assert(new FileInfo(path).Length == data.Length);
}

我会问,为什么你要把一个流读到一个字节[],如果你想复制一个流的内容,我可以建议使用MemoryStream和写你的输入流到内存流。

在. net Framework 4及以后版本中,Stream类有一个内置的CopyTo方法可供使用。

对于框架的早期版本,方便的helper函数是:

public static void CopyStream(Stream input, Stream output)
{
byte[] b = new byte[32768];
int r;
while ((r = input.Read(b, 0, b.Length)) > 0)
output.Write(b, 0, r);
}

然后使用上述方法之一复制到MemoryStream,并对其调用GetBuffer:

var file = new FileStream("c:\\foo.txt", FileMode.Open);


var mem = new MemoryStream();


// If using .NET 4 or later:
file.CopyTo(mem);


// Otherwise:
CopyStream(file, mem);


// getting the internal buffer (no additional copying)
byte[] buffer = mem.GetBuffer();
long length = mem.Length; // the actual length of the data
// (the array may be longer)


// if you need the array to be exactly as long as the data
byte[] truncated = mem.ToArray(); // makes another copy

编辑:最初我建议使用Jason的答案作为支持Length属性的Stream。但它有一个缺陷,因为它假设Stream将在一个Read中返回它的所有内容,这并不一定是真的(例如,对于Socket来说就不是这样)。我不知道BCL中是否有一个Stream实现的例子,它支持Length,但可能会以比你请求的更短的块返回数据,但由于任何人都可以继承Stream,这种情况很容易发生。

在大多数情况下,使用上面的通解可能更简单,但假设你确实想直接读入bigEnough数组:

byte[] b = new byte[bigEnough];
int r, offset;
while ((r = input.Read(b, offset, b.Length - offset)) > 0)
offset += r;

也就是说,反复调用Read并移动你将存储数据的位置。

你也可以尝试一次只读入部分,并展开返回的字节数组:

public byte[] StreamToByteArray(string fileName)
{
byte[] total_stream = new byte[0];
using (Stream input = File.Open(fileName, FileMode.Open, FileAccess.Read))
{
byte[] stream_array = new byte[0];
// Setup whatever read size you want (small here for testing)
byte[] buffer = new byte[32];// * 1024];
int read = 0;


while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
stream_array = new byte[total_stream.Length + read];
total_stream.CopyTo(stream_array, 0);
Array.Copy(buffer, 0, stream_array, total_stream.Length, read);
total_stream = stream_array;
}
}
return total_stream;
}
    byte[] buf;  // byte array
Stream stream=Page.Request.InputStream;  //initialise new stream
buf = new byte[stream.Length];  //declare arraysize
stream.Read(buf, 0, buf.Length); // read from stream to byte array

我知道的最简单的解决方法是:

using(var memoryStream = new MemoryStream())
{
sourceStream.CopyTo(memoryStream);
return memoryStream.ToArray();
}

好吧,也许我在这里遗漏了一些东西,但我是这样做的:

public static Byte[] ToByteArray(this Stream stream) {
Int32 length = stream.Length > Int32.MaxValue ? Int32.MaxValue : Convert.ToInt32(stream.Length);
Byte[] buffer = new Byte[length];
stream.Read(buffer, 0, length);
return buffer;
}

如果您从移动设备或其他发送文件

    byte[] fileData = null;
using (var binaryReader = new BinaryReader(Request.Files[0].InputStream))
{
fileData = binaryReader.ReadBytes(Request.Files[0].ContentLength);
}
Byte[] Content = new BinaryReader(file.InputStream).ReadBytes(file.ContentLength);

"bigEnough"数组有点拉伸。当然,缓冲区需要是“大分支”,但适当的应用程序设计应该包括事务和分隔符。在这种配置中,每个事务都有一个预设的长度,因此你的数组将预测一定数量的字节,并将其插入到正确大小的缓冲区中。分隔符将确保事务的完整性,并将在每个事务中提供。为了使应用程序更好,可以使用2个通道(2个套接字)。一个将通信固定长度的控制消息事务,该事务将包括使用数据通道传输的数据事务的大小和序列号信息。接收端将确认缓冲区的创建,然后才会发送数据。 如果你不能控制流发送者,那么你需要一个多维数组作为缓冲区。组件数组应该足够小,便于管理,并且根据您对预期数据的估计足够大,便于实际操作。流程逻辑将在后续元素数组中查找已知的开始分隔符和结束分隔符。一旦找到结束分隔符,将创建新的缓冲区来存储分隔符之间的相关数据,并且必须重新构造初始缓冲区以允许数据处理

只要代码转换流字节数组是一个下面。

Stream s = yourStream;
int streamEnd = Convert.ToInt32(s.Length);
byte[] buffer = new byte[streamEnd];
s.Read(buffer, 0, streamEnd);

我使用这个扩展类:

public static class StreamExtensions
{
public static byte[] ReadAllBytes(this Stream instream)
{
if (instream is MemoryStream)
return ((MemoryStream) instream).ToArray();


using (var memoryStream = new MemoryStream())
{
instream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
}

只需复制类到您的解决方案,您可以在每个流上使用它:

byte[] bytes = myStream.ReadAllBytes()

工作对我所有的流和节省了大量的代码! 当然,如果需要的话,您可以修改此方法以使用这里的一些其他方法来提高性能,但我喜欢保持简单