从流创建字节数组

Bob*_*Bob 860 c# inputstream .net-3.5

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

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

Stream s;
byte[] b;

using (BinaryReader br = new BinaryReader(s))
{
    b = br.ReadBytes((int)s.Length);
}
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*eet 1233

这真的取决于你是否可以信任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();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用.NET 4及更高版本,我会使用Stream.CopyTo,这基本上等同于我的代码中的循环 - 创建MemoryStream,调用stream.CopyTo(ms)然后返回ms.ToArray().任务完成.

我或许应该解释为什么我的回答比其他人长.Stream.Read并不保证它会读取它所要求的所有内容.例如,如果您正在从网络流中读取数据,它可能会读取一个数据包的值,然后返回,即使很快会有更多数据.BinaryReader.Read将一直持续到流的末尾或您指定的大小,但您仍然必须知道开始的大小.

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

有关更多信息(以及替代实现),请参阅此文章.

  • 请问具体为什么?16*1024`? (16认同)
  • @Jon,值得一提的是http://www.yoda.arachsys.com/csharp/readbinary.html (9认同)
  • @Jeff:我们这里没有真正的上下文,但是如果你一直在写一个流,那么是的,你需要在阅读前"回放"它.只有一个"光标"说明你在流中的位置 - 不是一个用于阅读而是另一个用于写作. (6认同)
  • @Jeff:这是来电者的责任.毕竟,流可能不是可搜索的(例如网络流),或者可能根本不需要倒回它. (5认同)
  • @just_name:我不知道这是否有任何意义,但是(16*1024)碰巧是Int16.MaxValue的一半:) (5认同)
  • @JonSkeet是否有特殊原因要使用“ 16 * 1024”? (2认同)
  • @RoyiNamir:在许多情况下,这只是一个合理的值,可以平衡吞吐量和内存。 (2认同)
  • @MohamedSakherSawan:该评论中没有足够的背景来真正帮助你.我建议你提出一个更详细的新问题. (2认同)

Nat*_*ips 695

虽然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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你的回答和乔恩之间有什么不同?此外,我必须执行此input.Position = 0以使CopyTo工作. (5认同)
  • @Jeff,我的回答只适用于.Net 4或更高版本,Jons将通过重写在更高版本中提供给我们的功能来处理较低版本.你是正确的,CopyTo只能从当前位置复制,如果你有一个Seekable流,你想从头开始复制,那么你可以使用你的代码或输入移动到开头.查询(0,SeekOrigin.Begin),虽然在很多情况下你的流可能不是可以搜寻的. (5认同)
  • 如果`input`已经是`MemorySteam`并且短路可能值得检查.我知道调用者传递一个`MemoryStream`会很愚蠢但是...... (5认同)
  • @Jodrell,确实如此.如果您将数百万个小流复制到内存中,其中一个是"MemoryStream",那么优化在您的上下文中是否有意义就是将进行数百万次类型转换所花费的时间与复制数据所需的时间进行比较这是一个`MemoryStream`到另一个`MemoryStream`. (3认同)
  • @nathan,从 Web 客户端读取文件 (filizesize=1mb) - iis 必须将整个 1mb 加载到其内存中,对吗? (2认同)
  • 没有理由释放 MemoryStream;这个答案除了对另一个答案提出错误的批评之外没有任何作用。 (2认同)

Fer*_*ira 105

只是想指出,如果你有一个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这实际上在Skeet的代码中引入了一个错误; 如果你调用`stream.Seek(1L,SeekOrigin.Begin)`,在你再次调用之前,如果流是一个内存流,你将获得比其他任何流更多的字节.如果调用者希望从当前位置到流的末尾读取,那么你不能使用`CopyTo`或`ToArray()`; 在大多数情况下,这不会是一个问题,但如果调用者不知道这种古怪的行为,他们会感到困惑. (4认同)
  • 那是对的,谢谢你指出了这一点.这一点仍然代表MemoryStream,所以我修复它以反映这一点. (3认同)
  • 呵呵,这么多点赞是为了什么?即使有最慷慨的假设,这只适用于已经是“MemoryStream”的流。当然,该示例在如何使用未初始化的变量方面显然也是不完整的。 (2认同)

小智 65

MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
Run Code Online (Sandbox Code Playgroud)

  • 应使用"new MemoryStream(file.PostedFile.ContentLength)"创建MemoryStream以避免内存碎片. (7认同)

Mr.*_*kin 49

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

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

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

  • 请注意,这在.NET 3.5及更低版本中不起作用,因为`CopyTo`在`Stream`上直到4.0才可用. (5认同)

Mic*_*l T 11

您甚至可以通过扩展程序使其变得更加漂亮:

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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将其称为常规方法:

byte[] arr = someStream.ToByteArray()
Run Code Online (Sandbox Code Playgroud)

  • 我认为将输入流放在使用块中是个坏主意.这个责任应由调用程序决定. (63认同)

önd*_*bay 9

将两个得票最高的答案合并到一个扩展方法中:

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();
    }            
}
Run Code Online (Sandbox Code Playgroud)


Nil*_*mar 9

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

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
Run Code Online (Sandbox Code Playgroud)

  • 仅当“dataInStream”已经是“MemoryStream”时这才有效 (4认同)

Sen*_*ith 8

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

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        var ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
Run Code Online (Sandbox Code Playgroud)


Bri*_*hey 7

我得到了鲍勃(即提问者)代码的编译时错误.Stream.Length是long,而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);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

上面的方法没问题……但是当您通过 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;
        }'
Run Code Online (Sandbox Code Playgroud)

  • 有点老了,但我觉得这一点值得一提 - @NothinRandom 的实现提供了字符串而不是流的工作。不过,在这种情况下,仅使用 File.ReadAllBytes 可能是最简单的。 (3认同)