三度、FirebirdのBlob型の操作について

解決


yTake  2014-08-04 14:57:12  No: 46511

yTakeです。

再び伺わせて下さい。

DBへ登録したBitMap画像を読み出しImageコンポーネントへ書き出す事を考えます。

DBのフィールドはblob型なので、ストリームで入出力する様にしました。

以下、抜粋です。

DB読み出し側:

var
    Bmp     : Array[ 1 .. 2 ] of TBitMap;
    Strm    : Array[ 1 .. 2 ] of TStream;
    
begin
    for i := 1 to 2 do
        Bmp[ i ]  :=  TBitMap.Create();

    with DataModule1.IBQuery do
    begin
    Open();
    for i := 1 to 2 do
    begin
      Strm[ i ] :=  CreateBlobStream( FieldByName('Image_1'), bmRead );

            Next();
    end;
    end;
    
    for i := 1 to 2 do
        Bmp[ i ].LoadFromStream( Strm[ i ]);

    for i := 1 to 2 do
        Strm[ i ].Free();

  Image1.Picture.Graphic  :=  Bmp[ 1 ];
  Image2.Picture.Graphic  :=  Bmp[ 2 ];

  for i := 1 to 2 do
      Bmp[ i ].Free();

end

因みに、登録側でもBitMapファイルから直接読み込まず、読み出し側に合わせてストリームを経由しました。(直接、BitMapファイルから読み込んでも変わりません)

DB登録側:

var
  bs  :  TMemoryStream;
begin
    bs := TMemoryStream.Create();

  do DataModule1.IBQuery with
  begin
  
      for i := 1 to 2 do
      begin
      bs.LoadFromFile( table_info[ i ].Image_filename );
      ParamByName( 'IMAGE_1' ).LoadFromStream( bs, ftBlob );
    end;
  
  end;

end;

画像は、フィールドへ正しく登録されてはいる様です。

DBGridとDBImageコンポーネントで、"IMAGE_1"フィールドを参照すると正しく画像が表示されています。
それを、BitMapへ書き出すと、何も表示されません。

画像データの取り扱いが間違っているでしょうか?

度々お世話になっていますが、やはり理解が不十分な様です。
ご教授頂ければと思います。

環境:
DELPHI XE3, Windouws7
Firebird 2.5


yTake  2014-08-04 17:54:37  No: 46512

自己レスです。

趣旨が分かり難いと思いました。

簡単に経緯に触れます。

読み出し側で、Imageコンポーネントにビットマップ画像を描画していますが、
このビットマップを、元々のビットマップファイルから読み込んで表示させると正しく表示されます。

blob型に正しく登録できていない可能性がある。
blob型のフィールドを、DBGridとDBImageコンポーネントを用いて参照すると、正しく画像が表示されます。

blob型のストリーム読み出しに問題がある?
コンパイル上や実行する上で、エラーはありません。

ここで行き詰まっています。

参考になるでしょうか?

よろしくお願いいたします。


au  2014-08-04 19:56:13  No: 46513

Imageコンポーネントとかで画像が表示されてるという事で外してる気もしますが。
登録側のメモリストリームに画像ファイルを読み込んだ後、Positionをリセットしてみたらどうでしょう?
bs.LoadFromFile( table_info[ i ].Image_filename );
//ここに追加
bs.Position := 0;
ParamByName( 'IMAGE_1' ).LoadFromStream( bs, ftBlob );


DEKO  2014-08-04 20:01:04  No: 46514

TBitmap に読み込まず、TMemoryStream に読み込んでSaveToFile() すると
ファイルが取り出せると思いますが、それは正しい Bitmap 画像になっているのですか?

DBGrid 等では画像は表示されるのに、0 バイトのファイルが
作られるのであれば、ストリームを先頭に移動させる必要があります。

[PDFファイルをFirebirdのBlobフィールドで使う。(Delphi freeml)]
http://www.freeml.com/delphi-users/2059?sid=cb92fe5d5fe8e1320f7f7833ce9d4643


yTake  2014-08-04 20:27:31  No: 46515

auさん
ありがとうございます。
ご指摘の点試してみましたが、変わりませんでした。

少し、状況が変わりました。

Blob型のストリーム読み出しをテストする為、IBQuery_newを作成しました。
もちろん、今回問題となっているIBQueyと同じ設定のはずです。
こちらで試したところ、Imageコンポーネントへ画像が描出できました。

ここで、今までのIBQueryに入れ替える形で、IBQuery_newを使ってみました。
当然、コンパイルは通り実行されるのですが、

Strm[ i ] :=  CreateBlobStream( FieldByName('Image_1'), bmRead );

の後、

for i := 1 to 2 do
    Bmp[ i ].LoadFromStream( Strm[ i ]);

で、ストリームエラーとなります。

"IBQuery"と"IBQuery_new"は完全に同じではないかもしれませんが、主従関係や参照テーブルは同じはずです。

もちろん、元へ戻すと、ストリ−ムエラーは出ませんが、画像も出ません。

両TIBQueryの違いを追う必要がありそうです。


yTake  2014-08-04 20:50:55  No: 46516

DEKOさん
ありがとうございます。

ご指摘の点、
    file_name :=  'c:\tmp.bmp';
    ms  :=  TMemoryStream.Create();
    ms.LoadFromStream( strm[ 1 ]);
    ms.SaveToFile( file_name );
の様に、試しました。

ところが、生成された画像ファイルがサイズが0バイトで、つまり空っぽでした。

先の私のレスで、
IBQueryの参照が間違っている可能性もありますが、正しいと思われるIBQuery_newでストリームエラーになる点が不可解です。

また、元々のIBQueryの方ですが、その他のフィールド情報はすべて正しく取り出せていますので、参照間違いとも考え難いのですが、、、


yTake  2014-08-04 21:05:40  No: 46517

auさん、DEKOさん

ありがとうございます。

DEKOさんの”DBGrid 等では画像は表示されるのに、0 バイトのファイルが
作られるのであれば、ストリームを先頭に移動させる必要があります。”

に対しては、auさんの”登録側のメモリストリームに画像ファイルを読み込んだ後、Positionをリセットしてみたらどうでしょう?”

が、その対処と思います。

既に、レスを上げていますが、Position=0では、変わりませんでした。

これ以外の意味でのストリームを先頭に移動させると言う事であれば、もう少しヒントを貰えるでしょうか?


au  2014-08-04 21:14:20  No: 46518

表示側のLoadFromStreamの前にも
Position:=0
を入れてみたらどうでしょ?


DEKO  2014-08-04 21:28:18  No: 46519

私が言っているのは表示側の事です。
提示したリンク先をご覧ください。


yTake  2014-08-04 21:34:10  No: 46520

auさん
ありがとうございます。
なるほど、表示側でも同じ処理をと言う事ですね。
試してみました、結果は変わりません。

今のところ、同じTImageコンポーネントに対し、同じTBitMap変数から画像を代入しています。
同じストリーム変数を用いて、同じテーブルのblob型フィールドから画像を読み題しています。
違いは、軽油のTIBQueryコンポーネントになります。

ただ、デバック上正常に表示されたIBQuery_newを、本ルーチンへ採用すると、ストリームエラーが表示される点が不可解です。
何か、見落としがあるのでしょうか?


yTake  2014-08-04 21:54:26  No: 46521

DEKOさん
ありがとうございます。

リンクは参照させて頂きました。
次の様にも試してみました。(seekを使いました)

    file_name :=  'c:\tmp.bmp';
    ms  :=  TMemoryStream.Create();
//    strm[ 1 ].Position  :=  0;
    strm[ 1 ].Seek( 0, soFromBeginning );
    ms.LoadFromStream( strm[ 1 ]);
    ms.SaveToFile( file_name );

これでも、"tmp.bmp"は、0バイトでした。

やり方が間違っているでしょうか?
ストリームの位置を先頭に戻すのはなるほどと思ったのですが。


DEKO  2014-08-04 22:02:57  No: 46522

記憶が定かではないのですが、
LoadFromStream した後のPosition はどこになっていますか?
(ブレイクポイントを張って ms にカーソルを合わせると位置を確認できます)

strm[1].Seek(0, soFromBeginning); // strm[1] を先頭に移動
ms.LoadFromStream(strm[1]);       // strm[1] を ms に読み込み
ms.Seek(0, soFromBeginning);      // ms を先頭に移動
ms.SaveToFile(file_name);         // ms の内容をファイルに保存

こうした場合にはどうなりますか?


DEKO  2014-08-04 22:10:45  No: 46523

一覧で見易い奴の方を貼っておきますね。

[PDFファイルをFirebirdのBlobフィールドで使う。(Delphi freeml)]
http://www.freeml.com/delphi-users/2059/latest


yTake  2014-08-04 22:20:48  No: 46524

DEKOさん
ありがとうございます。

msの方で、Position=0が趣旨ですね。

ブレイクポイントを設定し、"ms"上へカーソルを持っていくと、"ms|()"となっていて、値としては()に見えます。0では無い様にみえますが、0と言う事ですね。何故、0なのでしょう?
ステップを追って行っても、常に0です。

因みに、strm[i]の方は、"+strm|((),())"となっていて、"+"のところへカーソルを持っていくと、要素を参照できて、アドレスの様な、$から始まる8桁の16進数が縦に2つ表示されます。

ストリームの参照に失敗しているのでしょうか?

エラーはありません。


yTake  2014-08-04 23:05:11  No: 46525

DEKOさん

見易い参照をありがとうございました。

yTake


DEKO  2014-08-04 23:10:47  No: 46526

> ブレイクポイントを設定し、"ms"上へカーソルを持っていくと、"ms|()"となっていて
左に [+] が付いていて、それを展開するとPosition の値を見れたと思うのですが...。


DEKO  2014-08-04 23:24:44  No: 46527

> それを展開すると
XE3 とかだと展開しなくても FPosition でみれましたっけね。


yTake  2014-08-04 23:29:08  No: 46528

DEKOさん

msはTMemoryStreamで良いですか?

カーソルを持って行っても、[+]は出ていません。
strmの方は、[+]が出ますが、それでもアドレスの様な値のみで、それ以上変数名や[+]等は出ていません。

因みに、ビルド構成は”Debug”です。

何か設定が必要でしょか?

当方、XE3です。


DEKO  2014-08-04 23:32:41  No: 46529

> 値としては()に見えます
値が判らなければ、

・[表示 | デバッグ | ローカル変数]
・ShowMessage(IntToStr(ms.Position));
・OutputDebugString(PChar(IntToStr(ms.position))); <- IDE のイベントログに表示されます

いずれかの方法で実際の値を調べるのが間違いないですよ。


yTake  2014-08-04 23:33:47  No: 46530

すみません。
"FPosition"って何でしょうか?
監視式に追加して、内容を見ると言う事でしょうか?


DEKO  2014-08-04 23:36:07  No: 46531

デバッグ手法については、

"第21回デベロッパーキャンプ【T2】実践!Delphiデバッグテクニック" が
参考になると思います。

[PDF 資料]
http://edn.embarcadero.com/article/images/42116/T2_updated.pdf

[Youtube]
http://www.youtube.com/watch?v=h-x9-0cKPTs
http://www.youtube.com/watch?v=tn6q94g4pfs


yTake  2014-08-04 23:42:44  No: 46532

監視式に"ms.position"を追加しました。(監視式でも良いでしょうか?)

ソースコード行とカーソル行が1行ずれているのですが、
strm[1].Seek(0, soFromBeginning); 
ms.LoadFromStream(strm[1]);       
ms.Seek(0, soFromBeginning);      
ms.SaveToFile(file_name);         
の各段階で、
ms.Position=0
です。

つまり、先頭に居ると言う事になります。

これで画像が表示されないのは、データが存在しないからでしょうか?
DBImageでは表示されているのですが、、、


DEKO  2014-08-04 23:51:04  No: 46533

> "FPosition"って何でしょうか?
私の XE3 ではこうなるのですが…???
http://twitpic.com/e9hj6m

> これで画像が表示されないのは、データが存在しないからでしょうか?
Size (FSize) でストリームの大きさ (=画像サイズ) が得られると思いますが、
そちらはどうなっていますか?

> ソースコード行とカーソル行が1行ずれている
再構築するか、*.local を消してもう一度再構築してください。

ブレイクポイントのズレは つい最近 Facebook でも話題が出ましたが、

・シンボルファイルの読み込みエラー: *.dsm を削除
・ブレークポイントの不一致 (Win32): *.dsk または *.local を削除
・ブレークポイントの不一致 (リモートデバッグ): *.rsm の削除&再生成

これを行ってみてください。


yTake  2014-08-04 23:51:16  No: 46534

DEKOさん

デバック手法の案内をありがとうございます。参考になります。

YouTubeもあるのですね。知りませんでした。


DEKO  2014-08-04 23:55:36  No: 46535

> YouTubeもあるのですね。知りませんでした。
最近のは大体 Youtube 動画がありますヨ。

[とりあえず、デベロッパーキャンプの資料を読んでみようか。]
http://ht-deko.minim.ne.jp/tech044.html


yTake  2014-08-04 23:57:52  No: 46536

私のXE3では、msの右は"()"となっていて、変数が存在していません。

先にレスをした、()に見えて0と思ったのは、()で良かったと言う事でした。変数が存在していないので、0に見えた。

画像サイズ調べます。

行ずれの件、ありがとうございました。試してみます。

以後、しばらくアクセスできません。
また、追って、レスさせて頂きます。


igy  2014-08-05 08:32:12  No: 46537

関係ないかもしれませんが・・・

ヘルプ見ていたら、

IBX.IBCustomDataSet.TIBCustomDataSet.CreateBlobStream
http://docwiki.embarcadero.com/Libraries/XE6/ja/IBX.IBCustomDataSet.TIBCustomDataSet.CreateBlobStream

>メモ:  このメソッドが返すストリームは,データセットに属しています。このストリームは解放しないでください。
>        このメソッドが作成するストリームの割り当てと解放は,データセットが処理します。

とありますので、

>    for i := 1 to 2 do
>        Strm[ i ].Free();

をコメントアウトしてみては、いかがですか?

また、

>    Strm    : Array[ 1 .. 2 ] of TStream;

Strm    : Array[ 1 .. 2 ] of TIBBlobStream;

にしてみたら、どうなりますか?


igy  2014-08-05 09:27:36  No: 46538

あと、

>    with DataModule1.IBQuery do
>    begin
>        Open();
>        for i := 1 to 2 do
>        begin
>            Strm[ i ] :=  CreateBlobStream( FieldByName('Image_1'), bmRead );
>
>             Next();
>        end;
>    end;

データが2件ない場合、エラー処理を入れたほうがよいかもしれませんね。


yTake  2014-08-05 16:12:59  No: 46539

DEKOさん、igyさん
ありがとうございます。
yTakeです。

画像サイズに関しましては、"ms.Size"(ストリーム・サイズ?)の値を監視しました。
結果、Positionの時と同様に常に"0"です。

Strm[ i ].Free()をコメントアウトしてみましたが、変わりませんでした。

TStreamとTIBBlobStreamの件ですが、実は元々TIBBlobStreamでの実装を試みていましたが、使い方が分からず調べてみる内にTStreamでエラー無くコンパイルが通る様になりました。実際、独立的にデバッグするとTSreamで予定通りの動作が出来ている様に見えています。

TIBBlobStreamの場合、CreateやCreateBlobで引数の指定が無い様です。blobフィールド'IMAGE_1'の指定方法が分かりません。

TIBBlobStreamでも画像が取り出せればOKなのですが、、、

データが2件ない場合の処理も加えたいと思います。

またしばらくアクセスできません。
追って、レスさせて頂きます。


au  2014-08-05 19:50:54  No: 46540

よく見たら表示部分でBLOBからデータを取り出す前にNextでレコードを移動してるんですね。

恐らくこれが原因なんじゃないでしょうか。
var
    Bmp     : Array[ 1 .. 2 ] of TBitMap;
    Strm    : TStream;
    
begin
    for i := 1 to 2 do
        Bmp[ i ]  :=  TBitMap.Create();

    with DataModule1.IBQuery do
    begin
      Open();
      for i := 1 to 2 do
      begin
        Strm :=  CreateBlobStream( FieldByName('Image_1'), bmRead );
        Bmp[ i ].LoadFromStream( Strm[ i ]);
        Next();
      end;
    end;
Image1.Picture.Graphic  :=  Bmp[ 1 ];
Image2.Picture.Graphic  :=  Bmp[ 2 ];

for i := 1 to 2 do
      Bmp[ i ].Free();


yTake  2014-08-06 16:39:02  No: 46541

au様、みな様、

大変ありがとうございました。

ご指摘の様に修正しましたら、1枚目2枚目画像が共に表示されました。

Strm変数は、窓口みたいなものでその内容は保持していないのですね。

つまり、Strmを配列で定義して回そうと考えていた時点で私の方針に誤りがあったわけです。(ループで回しても、直ぐにBitMapへ書き出せばOKですね)

皆様、大変ありがとうございました。

"TIBBlobStream"も調べてみたいと思いますが、本件は、取り敢えず、閉めさせて頂きます。


yTake  2014-08-07 19:18:48  No: 46542

失礼致しました。

チェックを忘れていました。

皆様、ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加