入力ストリームからバイト配列を作成するための推奨される方法は何ですか?
これが、.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 4Stream.CopyTo
以降では、基本的にコード内のループと同等の を使用しMemoryStream
ます。ジョブ完了。stream.CopyTo(ms)
ms.ToArray()
私の答えが他の答えよりも長い理由を説明する必要があるかもしれません。Stream.Read
要求されたすべてを読み取ることを保証するものではありません。たとえば、ネットワーク ストリームから読み取りを行っている場合、すぐにデータが増える場合でも、1 パケット分の読み取りを行ってから戻る場合があります。BinaryReader.Read
ストリームの終わりまたは指定したサイズまで続行しますが、開始するサイズを知る必要があります。
上記のメソッドはMemoryStream
、データがなくなるまで読み取り (および a へのコピー) を続けます。次にMemoryStream
、配列内のデータのコピーを返すように要求します。開始するサイズがわかっている場合、または確実ではなくサイズを知っていると思わMemoryStream
れる場合は、をそのサイズで開始するように構成できます。同様に、最後にチェックを入れることができ、ストリームの長さがバッファーと同じサイズ ( によって返されるMemoryStream.GetBuffer
) の場合は、バッファーを返すことができます。したがって、上記のコードは完全には最適化されていませんが、少なくとも正しいでしょう。ストリームを閉じる責任は負いません。呼び出し元がそれを行う必要があります。
詳細(および代替実装)については、この記事を参照してください。
Jon の答えは正しいですが、彼は .NET に既に存在するコードを書き直していますCopyTo
。したがって、.Net 4 の場合は Sandip のソリューションを使用しますが、以前のバージョンの .Net の場合は Jon の回答を使用します。Sandip のコードはCopyTo
、多くの状況で例外が発生する可能性が非常に高く、破棄MemoryStream
されないままになるため、"using" を使用することで改善されます。
public static byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
あなたがすでに持っている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 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 は整数パラメーターを取ります。私の場合、長い精度を必要とするほど大きな 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);
}
上記のものは問題ありません...しかし、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();
}
}
}
ストリームが Length プロパティをサポートしている場合、バイト配列を直接作成できます。利点はMemoryStream.ToArray
、配列を 2 回作成することです。さらに、おそらくバッファ内の未使用の余分なバイトがいくつかあります。このソリューションは、必要な正確な配列を割り当てます。ストリームが 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;
}