从输入流创建字节数组的首选方法是什么?
这是我当前使用 .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
将一直持续到流结束或您指定的大小,但您仍然必须知道开始的大小。
上述方法将继续读取(并复制到 MemoryStream
)直到数据用完。然后它要求 MemoryStream
返回数组中数据的副本。如果您知道开始时的大小 - 或 认为 您知道大小,但不确定 - 您可以将 MemoryStream
构造为开始时的大小。同样,您可以在最后进行检查,如果流的长度与缓冲区的大小相同(由 MemoryStream.GetBuffer
返回),那么您可以只返回缓冲区。所以上面的代码不是很优化,但至少是正确的。它不承担关闭流的任何责任——调用者应该这样做。
有关详细信息(以及替代实现),请参阅 this article。
虽然 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();
}
}
input
已经是 MemorySteam
并且短路,可能值得检查。我知道调用者传递 MemoryStream
会很愚蠢,但是......
MemoryStream
,那么优化在您的上下文中是否有意义是进行数百万次类型转换所花费的时间与复制一个所花费的时间的比较这是一个 MemoryStream
到另一个 MemoryStream
。
只是想指出,如果您有一个 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);
}
}
MemoryStream
的流。当然,这个例子显然也是不完整的,它是如何使用一个未初始化的变量的。
stream.Seek(1L, SeekOrigin.Begin)
,在您充分调用之前,如果流是内存流,您将比任何其他流多获得 1 个字节。如果调用者希望从当前位置读取到流的末尾,那么您不能使用 CopyTo
或 ToArray()
;在大多数情况下,这不是问题,但如果调用者不知道这种古怪的行为,他们会感到困惑。
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();
}
}
}
将命名空间添加到配置文件并在您希望的任何地方使用它
CopyTo
在 4.0 之前在 Stream
上不可用。
您可以简单地使用 MemoryStream 类的 ToArray() 方法,例如
MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
dataInStream
已经是 MemoryStream
时才有效
你甚至可以通过扩展使它更漂亮:
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;
}
}
如果流支持 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 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();
}
}
由于此答案没有现代(即异步)版本,因此这是我为此目的使用的扩展方法:
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
调用一些内部方法这一事实。
将两个投票最多的答案组合成一个扩展方法:
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
localStream
不是 MemoryStream
怎么办?此代码将失败。
localStream
转换为 MemoryStream
,但 localStream
不是 MemoryStream
,它将失败.此代码可以正常编译,但在运行时可能会失败,具体取决于 localStream
的实际类型。您不能总是随意地将基类型转换为子类型; read more here。 This is another good example 解释了为什么您不能总是这样做。
FileStream
强制转换为 MemoryStream
,并且将失败并出现以下错误:“无法将 'System.IO.FileStream' 类型的对象强制转换为 'System.IO.MemoryStream' 类型。”示例:using (Stream fs = new FileStream(@"C:\pathtofile.txt", FileMode.Open)) { var memoryStream = (MemoryStream)fs; }
如果您只使用 var
,它将无法编译,因为它会隐式键入 MemoryStream
。如前所述,使用上面的 Stream
键入它会创建一个运行时异常。试试看,自己看看。
不定期副业成功案例分享
16*1024
吗?