从流创建字节数组

发布于 2024-07-08 04:16:34 字数 224 浏览 5 评论 0 原文

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

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

Stream s;
byte[] b;

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

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

What is the prefered method for creating a byte array from an input stream?

Here is my current solution with .NET 3.5.

Stream s;
byte[] b;

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

Is it still a better idea to read and write chunks of the stream?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

跨年 2024-07-15 04:16:34

这实际上取决于您是否可以信任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 中),直到用完数据。 然后,它要求 MemoryStream 返回数组中数据的副本。 如果您知道起始大小 - 或者认为您知道大小,但不确定 - 您可以将 MemoryStream 构造为起始大小。 同样,您可以在末尾进行检查,看看流的长度是否与缓冲区的大小相同(由 MemoryStream.GetBuffer) 那么你可以只返回缓冲区。 所以上面的代码并没有完全优化,但至少是正确的。 它不承担关闭流的任何责任 - 调用者应该这样做。

请参阅本文了解更多信息(以及替代实现)。

It really depends on whether or not you can trust s.Length. For many streams, you just don't know how much data there will be. In such cases - and before .NET 4 - I'd use code like this:

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

With .NET 4 and above, I'd use Stream.CopyTo, which is basically equivalent to the loop in my code - create the MemoryStream, call stream.CopyTo(ms) and then return ms.ToArray(). Job done.

I should perhaps explain why my answer is longer than the others. Stream.Read doesn't guarantee that it will read everything it's asked for. If you're reading from a network stream, for example, it may read one packet's worth and then return, even if there will be more data soon. BinaryReader.Read will keep going until the end of the stream or your specified size, but you still have to know the size to start with.

The above method will keep reading (and copying into a MemoryStream) until it runs out of data. It then asks the MemoryStream to return a copy of the data in an array. If you know the size to start with - or think you know the size, without being sure - you can construct the MemoryStream to be that size to start with. Likewise you can put a check at the end, and if the length of the stream is the same size as the buffer (returned by MemoryStream.GetBuffer) then you can just return the buffer. So the above code isn't quite optimised, but will at least be correct. It doesn't assume any responsibility for closing the stream - the caller should do that.

See this article for more info (and an alternative implementation).

扬花落满肩 2024-07-15 04:16:34

虽然 Jon 的答案是正确的,但他正在重写 CopyTo 中已存在的代码。 因此,对于 .Net 4,请使用 Sandip 的解决方案,但对于以前版本的 .Net,请使用 Jon 的答案。 Sandip 的代码可以通过使用“using”进行改进,因为在许多情况下,CopyTo 中的异常很可能会发生,并且会使 MemoryStream 未被释放。

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

While Jon's answer is correct, he is rewriting code that already exists in CopyTo. So for .Net 4 use Sandip's solution, but for previous version of .Net use Jon's answer. Sandip's code would be improved by use of "using" as exceptions in CopyTo are, in many situations, quite likely and would leave the MemoryStream not disposed.

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
顾铮苏瑾 2024-07-15 04:16:34

只是想指出,如果您有 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);
    }
}

Just want to point out that in case you have a MemoryStream you already have memorystream.ToArray() for that.

Also, if you are dealing with streams of unknown or different subtypes and you can receive a MemoryStream, you can relay on said method for those cases and still use the accepted answer for the others, like this:

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}
浸婚纱 2024-07-15 04:16:34
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
煮茶煮酒煮时光 2024-07-15 04:16:34

只是我的几美分...我经常使用的做法是将这样的方法组织为自定义帮助程序,

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

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

just my couple cents... the practice that I often use is to organize the methods like this as a custom helper

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

add namespace to the config file and use it anywhere you wish

執念 2024-07-15 04:16:34

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

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

You can simply use ToArray() method of MemoryStream class, for ex-

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
又怨 2024-07-15 04:16:34

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

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

You can even make it fancier with extensions:

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

And then call it as a regular method:

byte[] arr = someStream.ToByteArray()
寂寞美少年 2024-07-15 04:16:34

我收到 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);
}

I get a compile time error with Bob's (i.e. the questioner's) code. Stream.Length is a long whereas BinaryReader.ReadBytes takes an integer parameter. In my case, I do not expect to be dealing with Streams large enough to require long precision, so I use the following:

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);
}
还不是爱你 2024-07-15 04:16:34

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

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

Combinig two of the most up-voted answers into an extension method:

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();
    }            
}
一枫情书 2024-07-15 04:16:34

如果有人喜欢它,这里是一个仅 .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();
    }
}

In case anyone likes it, here is a .NET 4+ only solution formed as an extension method without the needless Dispose call on the MemoryStream. This is a hopelessly trivial optimization, but it is worth noting that failing to Dispose a MemoryStream is not a real failure.

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        var ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
}
恏ㄋ傷疤忘ㄋ疼 2024-07-15 04:16:34

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

根据流是否支持查找在两个版本之间切换的完整代码。 它包括对位置和不可靠的长度的检查。 这可能会稍微降低速度。 在我的测试中,ToArrayAsyncDirect 的速度比 ToArrayAsyncGeneral 快大约 3 倍。

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

If a stream supports the Length property, a byte array can be directly created. The advantage is that MemoryStream.ToArray creates the array twice. Plus, probably some unused extra bytes in the buffer. This solution allocates the exact array needed. If the stream does not support the Length property, it will throw NotSupportedException exception.

It is also worth noting that arrays cannot be bigger than 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;
}

Complete code which switches between both versions based on whether the stream supports seeking or not. It includes checks for Position and unreliable Length. That might slightly reduce speed. In my tests ToArrayAsyncDirect is about 3 times faster compared to 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;
    }
}
喜爱皱眉﹌ 2024-07-15 04:16:34

上面的方法没问题……但是当您通过 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;
        }'

The one above is ok...but you will encounter data corruption when you send stuff over SMTP (if you need to). I've altered to something else that will help to correctly send byte for byte:
'

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;
        }'
难得心□动 2024-07-15 04:16:34

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

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

Create a helper class and reference it anywhere you wish to use it.

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}
纸短情长 2024-07-15 04:16:34

这是我正在使用、测试并且运行良好的功能。
请记住,“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;
        }
    }

This is the function which I am using, tested and worked well.
please bear in mind that 'input' should not be null and 'input.position' should reset to '0' before reading otherwise it will break the read loop and nothing will read to convert to array.

    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;
        }
    }
み青杉依旧 2024-07-15 04:16:34

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

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

In namespace RestSharp.Extensions there is method ReadAsBytes. Inside this method is used MemoryStream and there is the same code like in some examples on this page but when you are using RestSharp this is easiest way.

using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();
半步萧音过轻尘 2024-07-15 04:16:34

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

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

优化基于 ToArray 的源代码调用一些内部方法。

Since there's no modern (i.e. async) version of this answer, this is the extension method I use for this purpose:

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

The optimization is based on the fact the source code for ToArray calls some internal methods.

猫烠⑼条掵仅有一顆心 2024-07-15 04:16:34

您可以使用此扩展方法。

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

You can use this extension method.

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();
    }
}
暖阳 2024-07-15 04:16:34
MemoryStream stream = new MemoryStream();
// do what you want to save in stream buffer
//  ...
// then define byte array with specific size same as stream length.
byte[] readByte = new byte[stream.Length];
// copy all byte from stream to an byte array
readByte = stream.ToArray();
MemoryStream stream = new MemoryStream();
// do what you want to save in stream buffer
//  ...
// then define byte array with specific size same as stream length.
byte[] readByte = new byte[stream.Length];
// copy all byte from stream to an byte array
readByte = stream.ToArray();
溺深海 2024-07-15 04:16:34

我能够让它在一行上工作:

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

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

i was able to make it work on a single line:

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

as clarified by johnnyRose, Above code will only work for MemoryStream

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文