程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi數據庫編程新手指南(03)

Delphi數據庫編程新手指南(03)

編輯:Delphi

 第一節處理BLOBs(很大的二進制數據塊),在Access中存儲圖片

現在的數據庫應用程序不僅僅只需要處理文本或數字數據。例如,基於Interner/Intranet或多媒體的應用開發,就需要頻繁的顯示數據庫中的文字以及圖片。在這一章中,我們將了解怎樣通過ADO取出並顯示Access數據庫中的圖形數據(圖像)。不用擔心,即使它所需的數據庫編程技巧已遠超前面課程所學。

如果你是從本教程的開頭部分學起(尤其是第二章),你就會知道怎樣連接數據庫,並在DBGrid中顯示Applications表(來源於我們的aboutdelphi.mdb數據庫)。記住我們用到的三個數據組件:DBGrid、ADOTable和DataSource。回到第一章,我們創建數據庫時,將Applications表的最後一個字段留為了空(在用一些虛擬的數據填充數據庫後)。其字段名為Picture,類型為OLE對象類型。

向右滾到DBGrid的最後一列,你將看到如圖所示內容:

 

使用MS Access時,我們可以在一個OLE對象類型的字段中存儲圖像(以及其他大型數據對象,如聲音視頻等)。該類型的數據被視為Binary Large Object Bitmap (BLOB)大型二進制位圖對象。在處理圖像時,可使用多種類型的圖片格式。最常用的有JPEG、GIF和BMP。其中JPEG已被網頁設計者廣泛采用,因其所需的存儲空間很小(換句話說,JPEG比BMP要小很多)。當然,BMP、GIF以及JPEG等圖形格式,Delphi都能處理。接下來,我們主要講解JPEG文件格式的處理。

 

第二節 在Access中存儲圖片(Storing picturesin Access)

  在討論如何在Delphi窗口中顯示數據庫表中的圖像前,讓我們先往數據庫裡增加一些圖形數據。運行Access,打開aboutdelphi.mdb庫。打開Applications表(應有一行數據)並選中Picture字段。

 

 

按下面的步驟增加圖像:

1、  單擊右鍵→插入對象→選擇“由文件創建”;

 

 

2、點擊“浏覽”→出現一個浏覽對話框選→找到你想插入的.jpg文件→確定→確定;

注:Picture字段的文本內容為一個可執行的、用於處理計算機上JPEG文件的名稱。當然,在表格上你不會看見圖片。若要查看圖片,雙擊該字段,關聯應用程序即會打開該JPG文件。

  現在,我們數據庫裡有了圖片,接下來讓我們在Delphi窗體中顯示它。使用第二章帶有數據組件的Delphi窗體。

 

第三節  用DBImage引出JPEG —— 錯誤方法

     DBImage ——思路(The DBImage - takeone)

當試圖使用Delphi做新的嘗試時,首先想到向其自帶幫助尋求辦法。文檔內容:TDBImage(組件面板的Data Controls頁)表示數據庫當前記錄中一個BLOB字段的圖形圖像。使用TDBImage表示圖形字段值。TDBImage允許窗體顯示數據庫中的圖形數據。TDBImage僅僅比TImage組件多了一些數據可視屬性。其中最重要的兩個屬性是:DataSource(數據源)和Field(字段)。DataSource屬性將圖形組件連接到數據庫。在我們的窗體上有一個名為DataSource1的DataSource組件——代表一個數據集。Field屬性表示保存圖像的字段(在表中)。

一切都清楚了,現在,在窗體上放置一個DBImage組件並使用默認名DBImage1。為了真正的使DBImage與表的BLOB字段相連,需要做以下配置(使用Object Inspector):

 DBImage1.DataSource = DataSource1

DBImage1.DataField = Picture

為了顯示儲存在Applications表中Picture字段的JPEG圖像,這是必要訣竅。

為了驗證此配置是否可用,將ADOTable1組件的Active屬性設為True(在Object Inspector中)。一旦這樣做,將出現以下錯誤提示框:

 

為什麼會顯示“位圖圖像無效”呢?我們有JPEG圖片而不是BMP圖片——問題就在這麼?讓我們再看看幫助。

     通過查找幫助文檔得出:①為了得到數據庫裡的JPG圖片,我們需使用TJpegImage對象;②為了顯示圖片,需要簡單、不可視版本的Image組件;③同時,還需使用流(Stream)從BLOB對象中載出圖片。幫助文檔敘述:應使用TADOBlobStream來訪問或改變ADO數據集中BLOB或memo(備注)字段的值。

 

第三節 用流引出JPEG—錯誤的方法

      引出JPEG—思路二(Pullingthe Jpeg - take two!)

      既然不能使用DBImage——那從窗體中刪除它並放一個普通的TImage組件(Additional頁)命名為ADOImage。不幸的是,Image組件沒有任何數據可視化屬性,因此,需要一個單獨的方法來顯示數據庫表中的圖片。最簡單的方法是:在窗體上添加一個Button組件,將程序代碼放在其OnClick事件中,按鈕名稱為:“btnShowImage”。

為了使用ADOBLOBStream,幫助文檔建議創建一個TADOBlobStream實例,用“流”的方式從數據庫中讀取圖形字段,然後釋放BLOB流。在中間的某個地方,我們將需要用LoadFromStream方法從TADOBlobStream對象中載入JPEG圖像。Image組件的Picture、Graphic屬性將用於存儲和顯示圖片。

 字段對象,它是什麼?

在Delphi數據庫程序的開發中,主要對象之一就是TField對象。字段組件是表示運行(或設計)時的數據集字段的非可視化對象。TADOTable(和其他TDataSet子類)提供設計時對Fields Editor(字段編輯器)的訪問方法。Fields Editor使你能選擇數據集中你所想包含的字段。更重要的是,它創建了應用程序數據集中使用的字段組件的穩固的列表。為了調用Fields Editor,可以雙擊TADOTable組件。默認情況下,字段列表是空的。點擊Add按鈕打開一個對話框,裡面列出了Applications表的字段列表。缺省情況下,所有字段都被選擇,然後選擇OK。

      Delphi會按如下的方式給出字段的默認名稱:Table(表)名+Field(字段)名。這意味著我們的圖片字段名為:ADOTable1Picture。

      TADOBlobStream的Create(創建)方法創建一個實例用於讀或寫一個指定的BLOB字段對象,在這裡是ADOTable1Picture字段。

      我們在btnShowImage按鈕的OnClick事件中寫入程序代碼。該代碼將從當前所選行的Picture字段中讀取圖片。源代碼如下所示:


[delphi] 
      uses jpeg; 
      ... 
      procedure TForm1.btnShowImageClick(Sender:TObject); 
      var 
bS:TADOBlobStream; 
        Pic :TJpegImage; 
      begin 
        bS:= TADOBlobStream.Create(ADOTable1.fieldbyname('Picture') as TBlobField,bmRead); 
        try 
          Pic:=TJpegImage.Create; 
           try 
           <SPAN style="WHITE-SPACE: pre">  </SPAN>Pic.LoadFromStream(bS); 
            <SPAN style="WHITE-SPACE: pre"> </SPAN>ADOImage.Picture.Graphic:=Pic; 
          finally 
           <SPAN style="WHITE-SPACE: pre">  </SPAN>Pic.Free; 
          end; 
       finally 
         bS.Free 
       end; 
     end; 

      uses jpeg;
      ...
      procedure TForm1.btnShowImageClick(Sender:TObject);
      var
bS:TADOBlobStream;
        Pic :TJpegImage;
      begin
        bS:= TADOBlobStream.Create(ADOTable1.fieldbyname('Picture') as TBlobField,bmRead);
        try
          Pic:=TJpegImage.Create;
           try
            Pic.LoadFromStream(bS);
             ADOImage.Picture.Graphic:=Pic;
          finally
            Pic.Free;
          end;
       finally
         bS.Free
       end;
     end;
      OK,讓我們運行這個工程。當然,設置ADOTable1.Active屬性為True。表單顯示後,點擊按鈕,將出現下面的顯示:

 

 

      呃, 怎麼哪?代碼百分之百的正確但為什麼不顯示圖像呢!記住“永不放棄,永不投降”!讓我們深入到字節水平看看到底發生了什麼!

 

 第四節  在BLOB中尋找JPEG的開端

      OLE對象類型格式—思路三(OLEobject type format - take three!)
      現在我們需要做的是把圖片存儲到磁盤(存為普通的二進制文件)並了解它裡面的內容是什麼。

      所有的圖片文件(格式)都有用來作為唯一標識的文件頭。JPG以所謂的SOI標記開始,該標記的十六進制值是$FFD8。

下面一行代碼存儲Picture字段的值到工作目錄的相關文件(BlobImage.dat)。在窗體的OnCreate事件中放置這條代碼,開始工程以後再移除該代碼。

     (ADOTable1.fieldbyname('Picture')as TBlobField).SaveToFile('.\BlobImage.dat');

      一旦我們有了這個文件。我們就可以使用Hex editor看它的內容。

 

 

      MSAccess把連接的OLE對象的路徑作為對象定義的一部分存儲在OLE對象字段中。因為OLE對象的存儲定義沒有被文檔化,所以沒有辦法知道真正的圖像數據被寫之前能得到什麼。

      分兩部分考慮。第一:我們需要找到'FFD8'並從那兒開始讀取圖像。第二:'FFD8'不可能總在文件的同一個位置。結論:我們需要一個函數,返回Access數據庫中存儲為OLE對象的JPG文件的SOI標記的位置。

      正確的方法—思路四(The correct way - take four!)

      提供了Blob類型字段後,函數應返回ADOBlobStream中'FFD8'字符串的位置。ReadBuffer(讀緩沖區)從流中一個字節一個字節的讀取數據。對ReadBuffer的每個調用都會一個字節一個字節的移動流的位置。當兩個字節一起引出SOI標記時,函數返回流的位置。函數:


[delphi] 
functionJpegStartsInBlob(PicField:TBlobField):integer; 
var 
   bS     : TADOBlobStream; 
   buffer : Word; 
   hx     : string; 
begin 
 Result := -1; 
  bS:= TADOBlobStream.Create(PicField, bmRead); 
  try 
   while (Result = -1) and (bS.Position + 1 < bS.Size) do 
   begin 
     bS.ReadBuffer(buffer, 1); 
     hx:=IntToHex(buffer, 2); 
     if hx = 'FF' then 
     begin 
      bS.ReadBuffer(buffer, 1); 
      hx:=IntToHex(buffer, 2); 
      if hx = 'D8' then 
        Result := bS.Position - 2 
      else if hx = 'FF' then 
        bS.Position := bS.Position-1; 
     end; //if  
   end; //while  
 finally 
   bS.Free 
 end; //try  
end; 

functionJpegStartsInBlob(PicField:TBlobField):integer;
var
   bS     : TADOBlobStream;
   buffer : Word;
   hx     : string;
begin
 Result := -1;
  bS:= TADOBlobStream.Create(PicField, bmRead);
  try
   while (Result = -1) and (bS.Position + 1 < bS.Size) do
   begin
     bS.ReadBuffer(buffer, 1);
     hx:=IntToHex(buffer, 2);
     if hx = 'FF' then
     begin
      bS.ReadBuffer(buffer, 1);
      hx:=IntToHex(buffer, 2);
      if hx = 'D8' then
        Result := bS.Position - 2
      else if hx = 'FF' then
        bS.Position := bS.Position-1;
     end; //if
   end; //while
 finally
   bS.Free
 end; //try
end;
   一旦有了SOI標記的位置信息,就能在ADOBlob流中找到圖片的位置。


[delphi] 
     uses jpeg; 
     ... 
procedure TForm1.btnShowImageClick(Sender:TObject); 
var 
 bS  : TADOBlobStream; 
  Pic: TJpegImage; 
  x :integer; 
begin 
  bS:= TADOBlobStream.Create(ADOTable1.fieldbyname('Picture') as TBlobField,bmRead); 
  try 
    x:= JpegStartsInBlob(ADOTable1.fieldbyname('Picture') as TBlobField); 
   bS.Seek(x, soFromBeginning); 
   Pic:=TJpegImage.Create; 
   try 
    Pic.LoadFromStream(bS); 
    ADOImage.Picture.Graphic:=Pic; 
   finally 
    Pic.Free; 
   end; 
 finally 
   bS.Free 
 end; 
end; 

     uses jpeg;
     ...
procedure TForm1.btnShowImageClick(Sender:TObject);
var
 bS  : TADOBlobStream;
  Pic: TJpegImage;
  x :integer;
begin
  bS:= TADOBlobStream.Create(ADOTable1.fieldbyname('Picture') as TBlobField,bmRead);
  try
    x:= JpegStartsInBlob(ADOTable1.fieldbyname('Picture') as TBlobField);
   bS.Seek(x, soFromBeginning);
   Pic:=TJpegImage.Create;
   try
    Pic.LoadFromStream(bS);
    ADOImage.Picture.Graphic:=Pic;
   finally
    Pic.Free;
   end;
 finally
   bS.Free
 end;
end;
運行工程,OK!

 

 

   現在誰會說編程沒有趣味?

      注:在真正的代碼程序中,我們會在TDataSet的AfterScroll事件中加入代碼用於從當前行中讀取和顯示圖像(它在ADOTable1AfterScroll事件過程中)。當應用程序從一個記錄滾到另一個時,AfterScroll事件發生。

    思路五!

    這就是本章的主要內容。現在你可以存儲和顯示所有你感興趣的JPG圖片。在這篇文章的最後一頁,我會提供完整的代碼(form1單元);所有的數據安排都放在表單的OnCreate事件中。這確保了所有的三個組件被正確連接—在設計時你不需要使用Object Inspector(對象檢視器)。

    我承認,這一章不適合初學者,但世界是殘酷的!另一件事:你注意到最後你都不知道怎樣改變(或增加一些新的)表中的圖片!是的,那又是另一個完整的故事了!

 


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved