DOBON.NET DOBON.NETプログラミング掲示板過去ログ

ストリームの途中から始まるデータを読み込むには

環境/言語:[.NET Framework 2.0]
分類:[.NET]

 System.Drawing.Imageクラスにはバイナリデータからイメージを作成するためのメソッド「Image.FromStream」を使ってファイル内のデータからイメージを作成しようと考えているのですが、この読込元のストリームの途中からイメージデータが格納されている場合、単純にStream.Seek()でストリーム内のポジションを移動させたStreamクラスを指定しただけでは、「"使用されたパラメータが有効ではありません。"」という例外がスローされます。

 こういった、「データの途中からイメージバッファが開始される」場合は、どのようにコーディングするのが最適なのでしょうか。

(失敗するコード例)
FileStream fs = new FileStream("test.dat", FileMode.Open, FileAccess.Read);
fs.Seek(100, SeekOrigin.Begin); //100バイト目から実際のイメージデータが存在
Image img = Image.FromStream(fs); //例外がスローされる
Streamから、BinaryReaderを使ってMemoryStreamに読み込むかな
> Streamから、BinaryReaderを使ってMemoryStreamに読み込むかな

 数百MBあるアーカイブファイルの中の10バイト目から始まる画像データを読み込む場合、MemoryStreamに残りバッファを全て転送することになるので、結構コストがかかりますね。となると、画像フォーマットごとに存在するイメージデータのサイズを取得するメソッドを作るしかないのでしょうか。
同じ機能を.NET1.1で作った事があるので。

FileStream.Seekしたあとに
FileStream.Readを使ってみてください
これで中博俊さんがおっしゃってる事が出来ます
> FileStream.Seekしたあとに
> FileStream.Readを使ってみてください
> これで中博俊さんがおっしゃってる事が出来ます

FileStream.Seek/ReadとMemoryStreamを使うということはこんな感じでしょうか。

//「0」ファイルの2バイト目から画像データが存在すると仮定
FileStream fs = new FileStream("D:\\0", FileMode.Open);
fs.Seek(2, SeekOrigin.Begin);
byte[] buf = new byte[fs.Length];
fs.Read(buf, 0, fs.Length);

MemoryStream ms = new MemoryStream();
ms.Write(buf, 0, buf.Length);

Image img = Image.FromStream(ms);

これだと「D:\0」画像自体のサイズが10KBなのに、ファイルが100MBもあった場合はやっぱり効率が悪いので、違いますよね(^^;
そんな感じです。
後はbyteのLengthの部分をいじればOKです

それとMemoryStreamをNewする時に
byte配列を引数にすればコードの短縮になりますよ。
と、書いた後に気づきました。
Lengthを指定する為に
>画像フォーマットごとに存在するイメージデータのサイズを取得するメソッドを作るしかないのでしょうか。

と、言う質問だったのですね。
勘違いしました、すいません。

ひとまとめになっているファイルのヘッダにでも
イメージのサイズを格納したデータを持たせて
そのデータを最初に読み取り、それを元にLengthを設定すればよいかと。

実際、私もこの方法で作りました
すくなくとも.NET Framework 2.0の時点ではストリームのはじめからデータがなければ画像の作成は無理と言うことですね。回答されたテクニックに基づいてプログラムを構築することにいたします。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板