从输入流创建字节数组的首选方法是什么?
这是我目前使用.NET 3.5的解决方案。
1 2 3 4 5 6 7
| Stream s ;
byte[] b ;
using (BinaryReader br = new BinaryReader (s ))
{
b = br .ReadBytes((int)s .Length);
} |
读取和写入流块仍然是一个更好的主意吗?
- 当然,另一个问题是您是否应该从流中创建一个byte[]…对于大数据,最好将流视为流!
这实际上取决于你是否可以信任s.Length。对于许多流,您只是不知道将有多少数据。在这种情况下,.NET 4之前,我会使用如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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返回),那么您可以只返回缓冲区。所以上面的代码并不是很优化,但至少是正确的。它不承担关闭流的任何责任-调用方应该这样做。
有关更多信息(以及其他实现),请参阅本文。
- @乔恩:如果你知道你的精确长度的流,它好到使用的代码的poster写的吗?
- @:用纯binaryreader,是的,就是将环轮直到流也空的或足够的数据已读。不要试着用流的方式,虽然。
- @乔恩,它可能是值得的mentioning yoda.arachsys.com / csharp / readbinary.html
- 乔恩@看来我必须设定的输入到0的位置。(input.position = 0)为读到的工作。另有可以在环也ignored。它normally,我需要复制/ persist"输入流和使用它为以后的吗?
- @杰夫:我们真的不有上下文的睾丸,但如果你一直writing到流,然后是你需要"倒带"它之前阅读。只是有一个"cursor说"你在哪里是在流-不是一个给阅读和separate一个给writing。
- @乔恩:如果它听起来愚蠢的,但不应该被"卷"部分readfully吗?或者是它的"责任"的caller本方法吗?
- @杰夫:"责任"的caller。毕竟,流可能不是seekable(例如网络流),或有可能只是简单的把它不需要倒带。
- 我可以问为什么16*1024具体来说吗?
- 只是_ @名称:我不知道如果这是腹部的任何significance,但(16×1024)happens到更高的int16.maxvalue:)
- achekh @:不,绝对不是。在每一个iteration,你可以读到一开始的阵列-因为唯一的数据的时候,也用上Write呼叫在同一iteration。
- 也没有任何特殊的理由jonskeet @为16*1024吗?
- royinamir @:这只是一reasonable价值在许多例,balancing throughput与记忆。
- 它也不知道,我的记忆的文件流:(
- mohamedsakhersawan @:??有足够的上下文在那个评论真的帮到你。我建议你问的问题与新的更多的细节。
- thanx乔恩,我已经问两个问题:stackoverflow.com /问题/ / 15229166 & hellip;和stackoverflow.com /问题/ 14953393 / hellip &;
- 我只是_ name的单项目也happens到使用10*1024在他们的实施。
- 我有一个问题,several给该解决方案:将它的工作也为文件,有several(hundreds)megabytes吗?和它的工作也将为多媒体文件(这样一个图片或音乐),与16kib buffer尺寸吗?
- @风暴:它发展成好的记忆,所以这对我有多depends记忆你已经得到了。但有足够的记忆,它会工作,为several -好吧。- MB的文件。和是的,16K buffer也统一为多媒体文件-它没有被支付任何注意到一种数据阅读所有的。这只是一个二进制流。
- jonskeet @你说在你的回答是:"Stream.Read并不保证T,它将一切都读它的问。如果你是从阅读的网络流,例如,它可能是值得一读的喷气背包,然后返回,即使我将有更多的数据很快。"我不确定我跟随,因为你是用这一方法在你的解决方案?我想念的东西或做睾丸?
- silkfire @:用的方法是好的,但你需要使用的回报价值找到了你已经读了多少数据,和甚至如果你读的smaller比我要求的是量,可能仍然是有意识的,读到更多的数据。
- 所有的,10*1024代替,这是完全由10 MB和太大的文本块,如果你问我,我decreased它到512(CB)和它的作品很好,我在正文的文件为1.5 MB。
- vapcguy @:嗯,10×1024 10 kilobytes,不megabytes 10。所以如果你设置它的512k,你increased它明显………………
- 谢谢jonskeet @ * clarification -但是我集它只是"512",就认为这是约,为一些原因。所以它的其实只有1 / 2部(1024 / 10),compared什么也显示睾丸,或百分之五岁大的块。我可能增加它,现在!:)
- @量子:请不要以这样有语义意义的方式编辑文章。您提出的更改可能会破坏代码:GetBuffer可以返回一个大于实际使用数据的缓冲区。此方法返回一个数组,该数组正好是所读取数据的大小。
虽然乔恩的回答是正确的,但他正在重写CopyTo中已经存在的代码。对于.NET 4,使用sandip的解决方案,但是对于.NET的早期版本,使用jon的答案。sandip的代码将通过使用来改进,因为在很多情况下,CopyTo中的例外情况非常可能,并且会使MemoryStream不被处理。
1 2 3 4 5 6 7 8
| public static byte[] ReadFully (Stream input )
{
using (MemoryStream ms = new MemoryStream ())
{
input .CopyTo(ms );
return ms .ToArray();
}
} |
- 你的答案和乔恩的有什么不同?另外,我必须做这个输入。位置=0,以便copyto工作。
- @Nathan,从Web客户端读取一个文件(filizesize=1MB)-IIS必须将整个1MB加载到其内存中,对吗?
- @Jeff,我的答案只适用于.NET 4或更高版本,jons将通过重写在更高版本中提供给我们的功能来处理较低版本。正确的做法是,copy to只能从当前位置进行复制,如果您有一个可查找的流,并且希望从开始位置进行复制,则可以使用代码或输入移动到开始位置。seek(0,seekOrigin.begin),但在许多情况下,您的流可能无法查找到。
- @Royinamir,您是正确的,这段代码的目的是将流的全部内容读取到内存中。我不确定您到底在问什么,但是如果您使用WebClient,并且在客户端使用此代码,将IIS作为服务器,则IIS不会将整个文件读取到内存中,但客户端会。
- 如果input已经是MemorySteam并短路,则可能值得检查。我知道打电话的人通过一个MemoryStream是愚蠢的,但是…
- @乔德雷尔同意,就像费尔南多·内拉的回答stackoverflow.com/a/2630539/1037948
- @Jodrell,这样做只是为了速度优化。关于速度优化的决定应该总是在预期使用的情况下做出。如果对MemoryStream执行检查使代码1/1000变慢,那么只有在MemoryStream可能每千次至少传入一次时才应执行检查,否则您将有一个总的减速。
- @纳森菲利普斯对我来说更像是一个联立方程。复制一个MemoryStream到一个MemoryStream的成本,难道不应该被考虑在内吗?
- @乔德雷尔,正是如此。如果您要将数百万个小流复制到内存中,其中一个是MemoryStream,那么在您的上下文中,优化是否有意义就是将进行数百万个类型转换所花费的时间与将MemoryStream复制到另一个MemoryStream所花费的时间进行比较。
- 没有理由丢弃一个memorystream;这个答案除了对另一个答案进行错误的批评之外,什么也没有做。
- 应该注意,@nathanphillips示例将从当前位置复制输入Stream。这可能会困住那些假设CopyTo()将复制整个流的人;可能需要设置stream.Position = 0;。CopyTo()
只是想指出,如果您有一个内存流,那么您已经有了一个memorystream.ToArray()。
另外,如果您处理的是未知或不同子类型的流,并且您可以接收到一个MemoryStream,那么您可以将上述方法用于这些情况,并且仍然可以对其他情况使用接受的答案,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12
| public static byte[] StreamToByteArray (Stream stream )
{
if (stream is MemoryStream )
{
return ((MemoryStream )stream ).ToArray();
}
else
{
// Jon Skeet's accepted answer
return ReadFully (stream );
}
} |
- 啊,所有的赞成票都是为了什么?即使有最宽泛的假设,这也只适用于已经是MemoryStreams的流。当然,这个示例在如何使用未初始化的变量方面也明显不完整。
- 是的,谢谢你的指点。不过,这一点仍然代表着MemoryStream,所以我修正了它以反映这一点。
- 只需提到,对于memoryStream,另一种可能是memoryStream.getBuffer(),尽管其中涉及一些gotchas。参见stackoverflow.com/questions/1646193/…和krishnabhargav.blogspot.dk/2009/06/…
- 这实际上在双向飞碟的代码中引入了一个bug;如果在可读调用之前调用stream.Seek(1L, SeekOrigin.Begin),如果流是内存流,则比任何其他流多获得1个字节。如果调用者希望从当前位置读取到流的末尾,那么您不能使用CopyTo或ToArray();在大多数情况下,这不是问题,但是如果调用者不知道这种奇怪的行为,他们将被混淆。
1 2 3 4
| MemoryStream ms = new MemoryStream ();
file .PostedFile.InputStream.CopyTo(ms );
var byts = ms .ToArray();
ms .Dispose(); |
- 应使用"new memoryStream(file.postedFile.contentLength)"创建memoryStream,以避免内存碎片化。
只是我的几分钱…我经常使用的实践是将类似这样的方法组织为自定义助手
1 2 3 4 5 6 7 8 9 10 11
| public static class StreamHelpers
{
public static byte[] ReadFully (this Stream input )
{
using (MemoryStream ms = new MemoryStream ())
{
input .CopyTo(ms );
return ms .ToArray();
}
}
} |
将命名空间添加到配置文件中,并在任何需要的地方使用它
- 请注意,这在.NET 3.5及以下版本中不起作用,因为CopyTo直到4.0才在Stream上可用。
您甚至可以通过扩展使其更高级:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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();
}
}
}
}
} |
然后作为常规方法调用它:
1
| byte[] arr = someStream.ToByteArray() |
- 我认为将输入流放入一个using块是一个坏主意。这种责任应该由呼叫程序来承担。
您可以简单地使用memoryStream类的toArray()方法,例如-
1 2
| MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray(); |
我得到一个编译时错误与鲍勃的(即,发问者的)代码。stream.length是长的,而binaryReader.readBytes采用整数参数。在我的例子中,我不希望处理的流足够大,需要很长的精度,因此我使用以下方法:
1 2 3 4 5 6 7 8 9 10
| 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);
} |
上面的一个是可以的…但是当你通过SMTP发送东西时,你会遇到数据损坏(如果你需要的话)。我已经改成了其他有助于正确逐字节发送的内容:’
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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 ;
}' |
- 我不知道这段代码在哪里可以避免数据损坏。你能解释一下吗?
- 假设您有一张图片,希望通过SMTP发送。您可能会使用base64编码。由于某种原因,如果将文件拆分为字节,文件就会损坏。但是,使用二进制读卡器将允许成功发送文件。
- 有点老,但我觉得这值得一提——实现@nothinrandom提供的是字符串,而不是流。不过,在这种情况下,只使用file.readallbytes可能是最简单的。
- 由于危险代码样式(没有自动处置/使用)而投反对票。
创建一个助手类,并在您希望使用它的任何地方引用它。
1 2 3 4 5 6 7 8 9 10 11
| 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时,这是最简单的方法。
1 2
| using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes(); |
如果有人喜欢它,这里有一个.NET 4+唯一的解决方案,它是一个扩展方法,没有对memoryStream进行不必要的Dispose调用。这是一个微不足道的无望优化,但值得注意的是,未能处理内存流并不是真正的失败。
1 2 3 4 5 6 7 8 9
| public static class StreamHelpers
{
public static byte[] ReadFully (this Stream input )
{
var ms = new MemoryStream ();
input .CopyTo(ms );
return ms .ToArray();
}
} |
您可以使用这个扩展方法。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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();
}
} |
这是我正在使用、测试和工作良好的功能。请记住,"input"不应为空,"input.position"在读取前应重置为"0",否则将中断读取循环,并且不会读取任何内容来转换为数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 ;
}
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static byte[] ToByteArray (Stream stream )
{
if (stream is MemoryStream )
{
return ((MemoryStream )stream ).ToArray();
}
else
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream ())
{
int read ;
while ((read = stream .Read(buffer, 0, buffer .Length)) > 0)
{
ms .Write(buffer, 0, read );
}
return ms .ToArray();
}
}
} |
- 您只需复制1和3答案中的代码,而不添加任何有价值的内容。请不要这样做。:)
- 当您添加代码时,也请稍后描述您建议的解决方案。
我可以让它在一条线上工作:
1
| byte [] byteArr= ((MemoryStream)localStream).ToArray(); |
如johnnyrose所阐明的,上述代码仅适用于memorystream
- 如果localStream不是MemoryStream呢?此代码将失败。
- localstream必须是基于流的对象。有关基于流的对象的更多信息,请访问stackoverflow.com/questions/8156896/&hellip;
- 我想说的是,如果你试着把localStream投给MemoryStream,但localStream不是MemoryStream,它就会失败。这段代码可以很好地编译,但在运行时可能会失败,这取决于localStream的实际类型。您不能总是任意地将基类型强制转换为子类型;请在此处阅读更多内容。这是另一个很好的例子,解释了为什么你不能总是这样做。
- 详细说明我上面的评论:所有的内存流都是流,但不是所有的流都是内存流。
- 所有基于流的对象都将流作为基类型。流本身总是可以转换为内存流。无论您尝试将哪个基于流的对象强制转换为Meomry流,它都应该始终有效。我们的目标是将流对象转换为字节数组。你能给我一个失败的小案子吗?
- 那是错误的。简单示例: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键入它会创建一个运行时异常。自己试试看。
- 如果您有更多问题,我们可能应该移动此对话进行聊天。否则,尝试我的代码,您将看到。这里使用CopyTo的例子是正确的-您答案中的代码只有在(localStream is MemoryStream) == true的情况下才能工作,如前面所述。
- 现在,我明白你的意思了,埃多克斯1〔3〕是前进的道路。谢谢你的澄清