ストリームからバイト配列を作成する


913

入力ストリームからバイト配列を作成するために推奨される方法は何ですか?

これが.NET 3.5での現在のソリューションです。

Stream s;
byte[] b;

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

ストリームのチャンクを読み書きする方が良いでしょうか?


60
もちろん、もう1つの質問は、ストリームからbyte []を作成する必要があるということです...大きなデータの場合は、ストリームをストリームとして扱うことをお勧めします。
Marc Gravell

2
実際、おそらくbyte []ではなくストリームを使用する必要があります。ただし、ストリームをサポートしないシステムAPIがいくつかあります。たとえば、ストリームからX509Certificate2を作成することはできません。それにバイト[](または文字列)を与える必要があります。この場合、x509証明書はおそらく大きなデータではないので問題ありません。
0xced

回答:


1294

それは本当にあなたが信頼できるかどうかにかかっています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要求されたすべてを読み取ることを保証するものではありません。たとえば、ネットワークストリームから読み取る場合、すぐにデータが増える場合でも、1つのパケットの値を読み取ってから戻る可能性があります。BinaryReader.Readストリームの最後または指定したサイズまで続行しますが、開始するサイズを知っている必要があります。

上記のメソッドはMemoryStream、データがなくなるまで読み取り(およびへのコピー)を続けます。次にMemoryStream、配列のデータのコピーを返すように要求します。はじめのサイズがわかっている場合、または確信が持てないがサイズがわかっていると思われる場合は、MemoryStreamを最初のサイズに構築できます。同様に、最後にチェックを置くことができ、ストリームの長さがバッファーと同じサイズ(によって返されるMemoryStream.GetBuffer)であれば、バッファーを返すだけです。したがって、上記のコードは完全に最適化されていませんが、少なくとも正しいでしょう。ストリームを閉じる責任はありません-呼び出し側はそれを行う必要があります。

詳細(および代替の実装)については、この記事を参照してください。


9
@ジョン、それはyoda.arachsys.com/csharp/readbinary.htmlに言及する価値があるかもしれません
サムサフラン

6
@Jeff:ここには実際のコンテキストはありませんが、ストリームに書き込んでいる場合は、読む前に「巻き戻す」必要があります。ストリーム内の現在の位置を示す「カーソル」は1つだけです-読み取り用ではなく、書き込み用の別のカーソルです。
Jon Skeet

5
@ジェフ:それは呼び出し元の責任です。結局のところ、ストリームはシークできない(ネットワークストリームなど)か、単に巻き戻す必要がない場合があります。
Jon Skeet、2012年

18
16*1024具体的に理由を尋ねてもらえますか?
Anyname Donotcare 2012

5
@just_name:これに意味があるかどうかはわかりませんが、(16 * 1024)はたまたまInt16.MaxValueの半分です:)
caesay

735

ジョンの答えは正しいですが、ジョンはすでにに存在するコードを書き換えています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();
    }
}

6
それはあなたの答えとジョンの違いは何ですか?また、CopyToを機能させるには、このinput.Position = 0を実行する必要があります。
Jeff

1
@nathan、Webクライアントからファイルを読み込む(filizesize = 1mb)-iisは1mb全体をメモリにロードする必要がありますか?
Royi Namir

5
@Jeff、私の答えは.Net 4以上でのみ機能します。Jonsはそれ以降のバージョンで提供される機能を書き換えることにより、下位バージョンで機能します。CopyToが現在の位置からのみコピーすることは正しいです。シーク可能なストリームがあり、最初からコピーしたい場合は、コードまたはinput.Seek(0、SeekOrigin.Begin)を使用して最初に移動できます。ただし、多くの場合、ストリームはシークできない可能性があります。
Nathan Phillips

5
inputがすでにMemorySteam短絡しているかどうかを確認する価値があるかもしれません。私はそれを渡すことは発信者の愚かであることを知ってMemoryStreamいますが...
Jodrell 2013年

3
@ジョドレル、そうだね。あなたはメモリに小さなストリーム数百万をコピーしていると、そのうちの一つがある場合MemoryStream、その後の最適化は、あなたのコンテキストで理にかなっているかどうかだ1コピーするのにかかる時間に対して型変換の何百万人を行うために要する時間を比較したものであるMemoryStreamにし別MemoryStream
Nathan Phillips

114

あなたがすでにそれを持っている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);
    }
}

1
ええと、すべての賛成票は何ですか?最も寛大な仮定があっても、これはすでにMemoryStreams であるストリームに対してのみ機能します。もちろん、この例は、初期化されていない変数をどのように使用しているかについても、明らかに不完全です。
Roman Starkov

3
そうだ、指摘してくれてありがとう。ただし、ポイントはまだMemoryStreamを表しているので、それを反映するように修正しました。
フェルナンドネイラ

MemoryStreamの場合、別の可能性としてMemoryStream.GetBuffer()が考えられますが、いくつかの落とし穴があります。stackoverflow.com/questions/1646193/…およびkrishnabhargav.blogspot.dk/2009/06/…を
RenniePet

4
これは実際にスキートのコードにバグをもたらします。を呼び出すとstream.Seek(1L, SeekOrigin.Begin)、後で呼び出す前に、ストリームがメモリストリームの場合、他のストリームの場合よりも1バイト多くなります。呼び出し元が現在の位置からストリームの終わりまで読み取ることを期待している場合は、CopyToorを使用しないでくださいToArray()。ほとんどの場合、これは問題にはなりませんが、発信者がこの風変わりな動作を知らない場合、混乱します。
2015

67
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();

9
メモリの断片化を回避するには、MemoryStreamを「new MemoryStream(file.PostedFile.ContentLength)」で作成する必要があります。
Dan Randolph

52

私のカップルセントだけ...私が頻繁に使用する習慣は、カスタムヘルパーとしてこのようなメソッドを整理することです

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

名前空間を設定ファイルに追加して、どこでも使用できます


5
これは、4.0までCopyToは利用できなかったため、.NET 3.5以下では機能しませんStream
Tim

16

MemoryStreamクラスのToArray()メソッドを簡単に使用できます。

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

10

拡張機能を使ってさらに洗練させることもできます。

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

67
入力ストリームをusingブロックに入れるのは悪い考えだと思います。その責任は呼び出し側の手順にあるべきです。
ジェフ

7

ボブ(つまり、質問者)のコードでコンパイル時エラーが発生します。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);
}

5

誰かがそれを気に入った場合のために、ここでは、MemoryStreamに対する不要なDispose呼び出しのない拡張メソッドとして形成された.NET 4+のみのソリューションを示します。これは絶望的なほど簡単な最適化ですが、MemoryStreamの破棄に失敗しても実際の失敗ではないことに注意してください。

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

3

上記のものは問題ありませんが、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;
        }'

このコードがデータの破損を回避する場所がわかりません。説明できますか?
Nippey

画像があり、SMTP経由で送信したいとします。おそらくbase64エンコーディングを使用します。何らかの理由で、ファイルをバイトとして分割すると、ファイルが破損します。ただし、バイナリリーダーを使用すると、ファイルを正常に送信できます。
NothinRandom 2012年

3
やや古いですが、これについて言及しているように感じました-@NothinRandomの実装は、ストリームではなく文字列を処理します。ただし、この場合はFile.ReadAllBytesを使用するのがおそらく最も簡単です。
XwipeoutX 2014

1
危険なコードスタイル(自動破棄/使用なし)のため、反対票を投じます。
arni 2017

悲しいことに-1だけが許可され、質問とは何の関係もありません。名前付きのファイル名パラメーター、破棄なし、読み取りバッファーなし、ファイルモードなし、バイト単位で読み取るバイナリリーダーなのはなぜですか。
AridaneÁlamo

2

ヘルパークラスを作成し、使用したい場所で参照します。

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

2

名前空間RestSharp.Extensionsには、メソッドReadAsBytesがあります。このメソッドの内部ではMemoryStreamが使用され、このページのいくつかの例のように同じコードがありますが、RestSharpを使用している場合はこれが最も簡単な方法です。

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

1

この拡張メソッドを使用できます。

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

1

これは私が使用し、テストし、うまく機能した機能です。'input'はnullであってはならず、 '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;
        }
    }

-1
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の回答からコードをコピーしただけで、貴重なものは何も追加されていません。しないでください。:)
CodeCaster 2017年

コードを追加するときは、提案するソリューションについても簡単に説明してください。
yakobom

-5

私はそれを単一の行で機能させることができました:

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

johnnyRoseで明記されているように、上記のコードはMemoryStreamでのみ機能します


2
localStreamはない場合はどうなりMemoryStreamますか?このコードは失敗します。
johnnyRose 2017年

localStreamは、ストリームベースのオブジェクトである必要があります。ストリームベースのオブジェクトの詳細については、こちらをご覧ください。stackoverflow.com/ questions / 8156896 /…
Abba

1
私が提案しようとしていたことは、あなたがキャストしようとした場合、あるlocalStreamにはMemoryStream、しかし、localStreamあるではないMemoryStream、それはなり失敗します。このコードは正常にコンパイルされますが、の実際のタイプによっては、実行時に失敗する可能性がありlocalStreamます。基本型を子型に任意にキャストできるとは限りません。詳細はこちらこれは、常にこれを行うことができない理由を説明するもう1つの良い例です。
johnnyRose 2017年

上記のコメントを詳しく説明します。すべてのMemoryStreamはStreamsですが、すべてのStreamsがMemoryStreamsであるとは限りません。
johnnyRose 2017年

すべてのStreamベースのオブジェクトは、基本タイプとしてStreamを持っています。また、ストリーム自体は常にメモリストリームに変換できます。Meomry Streamにキャストしようとするストリームベースのオブジェクトに関係なく、常に動作するはずです。ここでの目標は、ストリームオブジェクトをバイト配列に変換することです。失敗する一例を教えてくれませんか?
アバ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.