从输入流创建字节数组的首选方法是什么?
这是我当前使用 .NET 3.5 的解决方案。
Stream s;
byte[] b;
using (BinaryReader br = new BinaryReader(s))
{
b = br.ReadBytes((int)s.Length);
}
读取和写入流的块仍然是一个更好的主意吗?
从输入流创建字节数组的首选方法是什么?
这是我当前使用 .NET 3.5 的解决方案。
Stream s;
byte[] b;
using (BinaryReader br = new BinaryReader(s))
{
b = br.ReadBytes((int)s.Length);
}
读取和写入流的块仍然是一个更好的主意吗?
这真的取决于你是否可以信任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
),那么您可以只返回缓冲区。所以上面的代码不是很优化,但至少是正确的。它不承担关闭流的任何责任——调用者应该这样做。
有关更多信息(和替代实现),请参阅本文。
虽然 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();
}
}
只是想指出,如果你有一个 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);
}
}
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
只是我的几分钱......我经常使用的做法是将这样的方法组织为自定义助手
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
}
将命名空间添加到配置文件并在您希望的任何地方使用它
您可以简单地使用 MemoryStream 类的 ToArray() 方法,例如
MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
你甚至可以通过扩展使它更漂亮:
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()
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);
}
如果有人喜欢它,这里是一个仅 .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();
}
}
上面的那个没问题...但是当您通过 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;
}'
创建一个辅助类并在您希望使用它的任何地方引用它。
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
}
在命名空间 RestSharp.Extensions 中有方法 ReadAsBytes。在此方法中使用了 MemoryStream,并且与此页面上的某些示例中的代码相同,但是当您使用 RestSharp 时,这是最简单的方法。
using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();
这是我正在使用、测试和运行良好的功能。请记住,'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;
}
}
您可以使用此扩展方法。
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();
}
}
如果流支持 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;
}
将两个投票最多的答案组合成一个扩展方法:
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();
}
}
我能够让它在一条线上工作:
byte [] byteArr= ((MemoryStream)localStream).ToArray();
正如johnnyRose所澄清的那样,上面的代码仅适用于 MemoryStream