关于C#:从流创建字节数组

 2019-05-06 

Creating a byte array from a stream

从输入流创建字节数组的首选方法是什么?

这是我目前使用.NET 3.5的解决方案。

1
2
3
4
5
6
7
Stream s;
byte[] b;

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

读取和写入流块仍然是一个更好的主意吗?


这实际上取决于你是否可以信任s.Length。对于许多流,您只是不知道将有多少数据。在这种情况下,.NET 4之前,我会使用如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
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返回),那么您可以只返回缓冲区。所以上面的代码并不是很优化,但至少是正确的。它不承担关闭流的任何责任-调用方应该这样做。

有关更多信息(以及其他实现),请参阅本文。


虽然乔恩的回答是正确的,但他正在重写CopyTo中已经存在的代码。对于.NET 4,使用sandip的解决方案,但是对于.NET的早期版本,使用jon的答案。sandip的代码将通过使用来改进,因为在很多情况下,CopyTo中的例外情况非常可能,并且会使MemoryStream不被处理。

1
2
3
4
5
6
7
8
public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}


只是想指出,如果您有一个内存流,那么您已经有了一个memorystream.ToArray()

另外,如果您处理的是未知或不同子类型的流,并且您可以接收到一个MemoryStream,那么您可以将上述方法用于这些情况,并且仍然可以对其他情况使用接受的答案,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer
        return ReadFully(stream);
    }
}


1
2
3
4
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();


只是我的几分钱…我经常使用的实践是将类似这样的方法组织为自定义助手

1
2
3
4
5
6
7
8
9
10
11
public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

将命名空间添加到配置文件中,并在任何需要的地方使用它


您甚至可以通过扩展使其更高级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
                }
            }
        }
    }
}

然后作为常规方法调用它:

1
byte[] arr = someStream.ToByteArray()


您可以简单地使用memoryStream类的toArray()方法,例如-

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

我得到一个编译时错误与鲍勃的(即,发问者的)代码。stream.length是长的,而binaryReader.readBytes采用整数参数。在我的例子中,我不希望处理的流足够大,需要很长的精度,因此我使用以下方法:

1
2
3
4
5
6
7
8
9
10
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);
}

上面的一个是可以的…但是当你通过SMTP发送东西时,你会遇到数据损坏(如果你需要的话)。我已经改成了其他有助于正确逐字节发送的内容:’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
        }'


创建一个助手类,并在您希望使用它的任何地方引用它。

1
2
3
4
5
6
7
8
9
10
11
public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

在命名空间restsharp.extensions中有方法readasbytes。在这个方法的内部使用的是memoryStream,和这个页面上的一些示例中的代码相同,但是当您使用restSharp时,这是最简单的方法。

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


如果有人喜欢它,这里有一个.NET 4+唯一的解决方案,它是一个扩展方法,没有对memoryStream进行不必要的Dispose调用。这是一个微不足道的无望优化,但值得注意的是,未能处理内存流并不是真正的失败。

1
2
3
4
5
6
7
8
9
public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        var ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

您可以使用这个扩展方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
    }
}

这是我正在使用、测试和工作良好的功能。请记住,"input"不应为空,"input.position"在读取前应重置为"0",否则将中断读取循环,并且不会读取任何内容来转换为数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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;
        }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
            }
        }            
    }


我可以让它在一条线上工作:

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

如johnnyrose所阐明的,上述代码仅适用于memorystream