1008

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

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

Stream s;
byte[] b;

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

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

4

17 回答 17

1389

这真的取决于你是否可以信任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将一直持续到流结束或您指定的大小,但您仍然必须知道开始的大小。

上述方法将继续读取(并复制到 a 中MemoryStream),直到数据用完。然后它要求MemoryStream返回数组中数据的副本。如果您知道开始的大小 - 或者认为您知道大小,但不确定 - 您可以将 构建MemoryStream为开始的大小。同样,您可以在最后进行检查,如果流的长度与缓冲区的大小相同(由 返回MemoryStream.GetBuffer),那么您可以只返回缓冲区。所以上面的代码不是很优化,但至少是正确的。它不承担关闭流的任何责任——调用者应该这样做。

有关更多信息(和替代实现),请参阅本文。

于 2008-10-21T13:45:57.500 回答
827

虽然 Jon 的回答是正确的,但他正在重写已经存在于CopyTo. 所以对于 .Net 4 使用 Sandip 的解决方案,但对于以前版本的 .Net 使用 Jon 的答案。Sandip 的代码将通过使用“使用”来改进,因为CopyTo在许多情况下,很可能会留下未处理的异常MemoryStream

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
于 2011-07-05T16:45:39.760 回答
122

只是想指出,如果你有一个 MemoryStream 你已经拥有memorystream.ToArray()了。

此外,如果您正在处理未知或不同子类型的流并且您可以收到 a MemoryStream,您可以针对这些情况继续使用上述方法,并且仍然对其他情况使用已接受的答案,如下所示:

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}
于 2010-04-13T14:54:01.430 回答
73
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
于 2011-02-12T13:33:29.263 回答
55

只是我的几分钱......我经常使用的做法是将这样的方法组织为自定义助手

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

将命名空间添加到配置文件并在您希望的任何地方使用它

于 2013-02-18T16:01:22.587 回答
20

您可以简单地使用 MemoryStream 类的 ToArray() 方法,例如

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
于 2018-07-11T19:21:03.320 回答
14

你甚至可以通过扩展使它更漂亮:

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()
于 2012-07-18T17:01:12.830 回答
8

Bob(即提问者)的代码出现编译时错误。Stream.Length 是 long 而 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);
}
于 2011-05-31T00:55:31.820 回答
8

如果有人喜欢它,这里是一个仅 .NET 4+ 的解决方案,它作为扩展方法形成,无需对 MemoryStream 进行不必要的 Dispose 调用。这是一个无可救药的微不足道的优化,但值得注意的是,未能 Dispose 一个 MemoryStream 并不是真正的失败。

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        var ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
于 2018-10-17T22:57:13.013 回答
4

上面的那个没问题...但是当您通过 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;
        }'
于 2012-07-30T22:22:24.473 回答
4

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

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}
于 2018-09-20T13:28:44.783 回答
4

在命名空间 RestSharp.Extensions 中有方法 ReadAsBytes。在此方法中使用了 MemoryStream,并且与此页面上的某些示例中的代码相同,但是当您使用 RestSharp 时,这是最简单的方法。

using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();
于 2019-01-14T15:20:40.570 回答
3

这是我正在使用、测试和运行良好的功能。请记住,'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;
        }
    }
于 2019-01-10T02:47:26.717 回答
2

您可以使用此扩展方法。

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();
    }
}
于 2017-07-27T06:49:07.823 回答
2

如果流支持 Length 属性,则可以直接创建字节数组。优点是MemoryStream.ToArray创建了两次数组。另外,缓冲区中可能有一些未使用的额外字节。此解决方案分配所需的确切数组。如果流不支持 Length 属性,则会抛出NotSupportedException异常。

还值得注意的是,数组不能大于 int.MaxValue。

public static async Task<byte[]> ToArrayAsync(this Stream stream)
{
    var array = new byte[stream.Length];
    await stream.ReadAsync(array, 0, (int)stream.Length);
    return array;
}

根据流是否支持查找在两个版本之间切换的完整代码。

/// <summary>
/// Converts stream to byte array.
/// </summary>
/// <param name="stream">Stream</param>
/// <returns>Binary data from stream in an array</returns>
public static async Task<byte[]> ToArrayAsync(this Stream stream)
{
    if (!stream.CanRead)
    {
        throw new AccessViolationException("Stream cannot be read");
    }

    if (stream.CanSeek)
    {
        return await ToArrayAsyncDirect(stream);
    }
    else
    {
        return await ToArrayAsyncGeneral(stream);
    }
}

private static async Task<byte[]> ToArrayAsyncGeneral(Stream stream)
{
    using (var memoryStream = new MemoryStream())
    {
        await stream.CopyToAsync(memoryStream);
        return memoryStream.ToArray();
    }
}

private static async Task<byte[]> ToArrayAsyncDirect(Stream stream)
{
    var array = new byte[stream.Length];
    await stream.ReadAsync(array, 0, (int)stream.Length);
    return array;
}
于 2021-09-23T11:41:35.550 回答
-1

将两个投票最多的答案组合成一个扩展方法:

public static byte[] ToByteArray(this Stream stream)
{
    if (stream is MemoryStream)
        return ((MemoryStream)stream).ToArray();
    else
    {
        using MemoryStream ms = new();
        stream.CopyTo(ms);
        return ms.ToArray();
    }            
}
于 2017-05-17T13:42:28.890 回答
-7

我能够让它在一条线上工作:

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

正如johnnyRose所澄清的那样,上面的代码仅适用于 MemoryStream

于 2017-03-07T16:06:58.833 回答