【问题标题】:How to convert an Stream into a byte[] in C#? [duplicate]如何在 C# 中将 Stream 转换为 byte[]? [复制]
【发布时间】:2023-03-15 23:40:01
【问题描述】:

有没有简单的方法或方法在 C# 中将Stream 转换为byte[]

【问题讨论】:

  • 不是问题的真正答案,但如果您的 Stream 来自文件,您可以使用 File.ReadAllBytes(path) 在一行中获取字节数组。

标签: c# inputstream bytearray


【解决方案1】:

我知道的最短解决方案:

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

【讨论】:

  • 旁注:CopyTo 仅适用于 .NET Framework 4。
  • 是的。您可以使用 MemoryStream.GetBuffer() 来避免额外的副本,但请注意返回的数组大小不是数据的大小。
  • 如果源流的长度是预先知道的,最好用这个长度来指定MemoryStream的容量;内部缓冲区将具有适当的大小。如果长度未知,那么写入 MemoryStream 意味着在写入数据和扩展缓冲区时可能存在内部缓冲区的多个副本,在这种情况下,额外的 ToArray 副本不一定是主要问题。
  • MemoryStreamIDisposable - 它不应该被包裹在 using 中吗?
  • 已更正。在 MemoryStream 的情况下并没有真正的需要(在源代码中挖掘,它什么都不做),但这可能会改变。
【解决方案2】:

调用下一个函数

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

【讨论】:

  • 不确定我是否同意那里的 Length*2 缓冲区扩展策略。
  • 如果您希望能够读取任意长度的流,几乎所有这些都是必需的。您可以使用 List 并保存一些代码..
  • 一堆关注点在一个大方法中混合在一起。是的,这一切都必须完成,但并非全部在一个功能中完成。有可增长的字节数组和流读取。如果它们分开,则更容易正确。
  • 使用 MemoryStream 可以使代码变得更简单...
  • 看起来像是yoda.arachsys.com/csharp/readbinary.html的修改版
【解决方案3】:

我使用这个扩展类:

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

适用于我的所有流并节省大量代码! 当然,如果需要,您可以修改此方法以使用此处的一些其他方法来提高性能,但我喜欢保持简单。

【讨论】:

  • if (instream is MemoryStream) return ((MemoryStream) instream).ToArray();更改为: var ms = instream as MemoryStream; if (ms != null) return ms.ToArray();
  • 使用 C# 7 会更好:if (instream is MemoryStream memoryStream) return memoryStream.ToArray();
【解决方案4】:

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

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

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 并移动您将存储数据的位置。

【讨论】:

  • 当您可以只使用 List 和 AddRange() 时,为什么还要使用 memorystream?据我所知,它在引擎盖下的作用完全一样。
  • @DrJokepu - 因为流到流的复制通常在其他情况下很有用。您只需要编写一个方法,就可以获得流到流的复制和流到数组的复制。
  • @John Saunders - CopyStream 方法绝对不应该在其中包含 using 语句,因此这将是一个奇怪的请求。示例用法可能需要在 FileStream 上使用一个 - 但它可能不需要(取决于其余代码是否希望以某种方式重用相同的 FileStream)。
  • 当我突然得到一个赞成票时,我注意到它已经过期了,因为 Stream 现在有一个 CopyTo 方法,它完全符合 CopyStream 所做的事情。
【解决方案5】:
Byte[] Content = new BinaryReader(file.InputStream).ReadBytes(file.ContentLength);

【讨论】:

  • 我可能是错的,但这似乎比在内存中创建两个副本的 MemoryStream 方式更有效。
  • 取决于场景,您的示例非常特定于文件流,您可以确定内容长度。如果输入是流怎么办?因为 Readbytes 只接受 int32
  • 不错,但BinaryReader 是一次性的,所以应该使用using
【解决方案6】:
    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

【讨论】:

  • 如果我没记错的话,“读取”并不总是从流中读取全部可用量 - 例如请求 N 字节,返回 M 字节且 M msdn.microsoft.com/en-us/library/…
【解决方案7】:

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

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

【讨论】:

  • 对于此方法和@user734862 的方法,我收到以下错误:“此流不支持搜索操作”一个 System.NotSupportedException。我认为这可能是因为我正在从 http 位置读取文件,然后将其发回。当您在系统上处理文件时,情况可能会有所不同。
  • Stream.Read 方法可以读取的字节数少于您请求的字节数。您应该检查 Read 方法的返回值。
  • 从例如 Microsoft.SharePoint.Client.File.OpenBinaryDirect 返回的流通常一次只返回 500 左右字节,无论您的缓冲区有多大。你永远不应该忽略 Stream.Read 的返回值。
  • 但是仔细看代码。缓冲区是根据 stream.Length 信息创建的。对于流来说,它永远不会太大。它可能太小了(Int32.MaxValue 是它的最大尺寸),但在大多数情况下这不太可能发生。
  • 这个问题是 Stream.Read 并不总是读取 length 字节 - 它可以决定读取更少(并返回实际读取的字节数)。您必须在循环中调用它以使其通用!
【解决方案8】:

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

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

【讨论】:

  • 应该提到您实际上可以在任何 FileStream 上使用它。在 WPF 中,不能使用Request.Files[0].InputStream,但可以使用using (FileStream fs = new File.OpenRead(fileName)) { var binaryReader = new BinaryReader(fs); fileData = binaryReader.ReadBytes((int)fs.Length); }。感谢您的提示!
【解决方案9】:

快速而肮脏的技术:

    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 并将输入流写入内存流。

【讨论】:

  • 并非所有 Streams 都支持 Length 属性
  • 绝对不能保证 Read() 返回所有要读取的字节。
【解决方案10】:
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 的 workaround 进行修改。

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

【讨论】:

  • 如果它说读而不是写那就太好了!
  • 他确实说过阅读。他想把流转换成byte[],是Read,而不是Write。
  • 另一个问题(我刚刚记得)是 Read 方法可能不会一次性返回所有数据。
【解决方案11】:

您也可以尝试一次只读取部分内容并扩展返回的字节数组:

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

【讨论】:

    【解决方案12】:

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

    将流转换为字节数组的代码如下。

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

    【讨论】:

    • 和其他所有答案一样:永远不要在不检查实际读取的返回值的情况下执行 stream.Read()...
    猜你喜欢
    • 2018-01-15
    • 2021-12-05
    • 1970-01-01
    • 2018-03-25
    • 1970-01-01
    • 2016-04-16
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多