代码之家  ›  专栏  ›  技术社区  ›  Bob

从流创建字节数组

  •  1088
  • Bob  · 技术社区  · 16 年前

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

    这是我目前的解决方案。净3.5。

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

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

    19 回复  |  直到 8 年前
        1
  •  1373
  •   Jon Skeet    6 年前

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

    上述方法将继续读取(并复制到 内存流 )直到数据用完。然后它问 内存流 返回数组中数据的副本。如果你知道开始的尺寸,或者 认为 你知道尺寸,但不确定——你可以构造 内存流 从一开始就是这个尺寸。同样,您可以在末尾进行检查,如果流的长度与缓冲区的大小相同(由返回 MemoryStream.GetBuffer )然后你可以直接返回缓冲区。所以上面的代码并没有完全优化,但至少是正确的。它不承担关闭流的任何责任——调用者应该这样做。

    看见 this article 了解更多信息(以及替代实现)。

        2
  •  809
  •   Noordeen xiyurui    6 年前

    虽然Jon的答案是正确的,但他正在重写中已经存在的代码 CopyTo 所以。Net 4使用Sandip的解决方案,但适用于以前版本的。网络使用乔恩的答案。Sandip的代码将通过使用“use”作为异常来改进 方法 在许多情况下,很可能会离开 MemoryStream 未处置。

    public static byte[] ReadFully(Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
    
        3
  •  121
  •   meJustAndrew    8 年前

    只是想指出,如果你已经有了一个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);
        }
    }
    
        4
  •  72
  •   Sandip Patel    14 年前
    MemoryStream ms = new MemoryStream();
    file.PostedFile.InputStream.CopyTo(ms);
    var byts = ms.ToArray();
    ms.Dispose();
    
        5
  •  55
  •   A. Wheatman    12 年前

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

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

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

        6
  •  21
  •   Nilesh Kumar    6 年前

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

    MemoryStream ms = (MemoryStream)dataInStream;
    byte[] imageBytes = ms.ToArray();
    
        7
  •  14
  •   Michal T    13 年前

    您甚至可以通过扩展使其更加美观:

    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()
    
        8
  •  8
  •   Brian Hinchey    14 年前

    Bob(即提问者)的代码在编译时出错。流。长度为长,而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);
    }
    
        9
  •  8
  •   SensorSmith    6 年前

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

    public static class StreamHelpers
    {
        public static byte[] ReadFully(this Stream input)
        {
            var ms = new MemoryStream();
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
    
        10
  •  4
  •   NothinRandom    13 年前

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

    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();
        }            
    }
    
        11
  •  4
  •   Draken R. Shilling    7 年前

    上面的那个没问题……但当你通过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;
            }'
    
        12
  •  4
  •   Wieslaw Olborski    6 年前

    创建一个辅助类,并在任何你想使用它的地方引用它。

    public static class StreamHelpers
    {
        public static byte[] ReadFully(this Stream input)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                input.CopyTo(ms);
                return ms.ToArray();
            }
        }
    }
    
        13
  •  3
  •   Fred.S    6 年前

    在名称空间RestSharp中。那里有ReadAsBytes方法的扩展。此方法内部使用MemoryStream,与本页某些示例中的代码相同,但当您使用RestSharp时,这是最简单的方法。

    using RestSharp.Extensions;
    var byteArray = inputStream.ReadAsBytes();
    
        14
  •  2
  •   Egemen Çiftci    8 年前

    如果流支持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;
    }
    

    根据流是否支持查找在两个版本之间切换的完整代码。它包括以下检查 Position 而且不可靠 。这可能会稍微降低速度。在我的测试中 ToArrayAsyncDirect 与以下各项相比,速度大约快3倍 ToArrayAsyncGeneral .

    public static class StreamExtensions
    {
        public static readonly byte[] TempArray = new byte[4];
    
        /// <summary>
        /// Converts stream to byte array.
        /// </summary>
        /// <param name="stream">Stream</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>Stream data as array</returns>
        /// <returns>Binary data from stream in an array</returns>
        public static async Task<byte[]> ToArrayAsync(this Stream stream, CancellationToken cancellationToken)
        {
            if (!stream.CanRead)
            {
                throw new AccessViolationException("Stream cannot be read");
            }
    
            if (stream.CanSeek)
            {
                return await ToArrayAsyncDirect(stream, cancellationToken);
            }
            else
            {
                return await ToArrayAsyncGeneral(stream, cancellationToken);
            }
        }
    
        /// <summary>
        /// Converts stream to byte array through MemoryStream. This doubles allocations compared to ToArrayAsyncDirect.
        /// </summary>
        /// <param name="stream">Stream</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns></returns>
        private static async Task<byte[]> ToArrayAsyncGeneral(Stream stream, CancellationToken cancellationToken)
        {
            using MemoryStream memoryStream = new MemoryStream();
            await stream.CopyToAsync(memoryStream, cancellationToken);
            return memoryStream.ToArray();
        }
    
        /// <summary>
        /// Converts stream to byte array without unnecessary allocations.
        /// </summary>
        /// <param name="stream">Stream</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>Stream data as array</returns>
        /// <exception cref="ArgumentException">Thrown if stream is not providing correct Length</exception>
        private static async Task<byte[]> ToArrayAsyncDirect(Stream stream, CancellationToken cancellationToken)
        {
            if (stream.Position > 0)
            {
                throw new ArgumentException("Stream is not at the start!");
            }
    
    
            var array = new byte[stream.Length];
            int bytesRead = await stream.ReadAsync(array, 0, (int)stream.Length, cancellationToken);
    
            if (bytesRead != array.Length ||
                await stream.ReadAsync(TempArray, 0, TempArray.Length, cancellationToken) > 0)
            {
                throw new ArgumentException("Stream does not have reliable Length!");
            }
    
            return array;
        }
    }
    
        15
  •  0
  •   adsamcik    4 年前

    这是我正在使用、测试和运行良好的功能。 请记住,在读取之前,'input'不应为null,'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;
            }
        }
    
        16
  •  -2
  •   önder çalbay    8 年前

    由于这个答案没有现代(即异步)版本,这是我为此目的使用的扩展方法:

    public static async Task<byte[]> ReadAsByteArrayAsync(this Stream source)
    {
        // Optimization
        if (source is MemoryStream memorySource)
            return memorySource.ToArray();
    
        using var memoryStream = new MemoryStream();
        await source.CopyToAsync(memoryStream);
        return memoryStream.ToArray();
    }
    

    优化是基于以下事实 source code for ToArray 调用一些内部方法。

        17
  •  -7
  •   Community CDub    8 年前

    您可以使用此扩展方法。

    public static class StreamExtensions
    {
        public static byte[] ToByteArray(this Stream stream)
        {
            var bytes = new List<byte>();
    
            int b;
    
            // -1 is a special value that mark the end of the stream
            while ((b = stream.ReadByte()) != -1)
                bytes.Add((byte)b);
    
            return bytes.ToArray();
        }
    }
    
    推荐文章