天天看點

vc實作圖檔的資料庫儲存(轉載)

vc進行圖檔的資料庫存儲 2007-03-10 11:27

1 前言

資料庫在實際的開發過程中常常需要存儲較大的二進制資料對象,如圖像、音頻檔案、視訊檔案或其他二進制資料,這些資料稱之為二進制大對象BLOB(Binary Large Object),其存取方式與普通資料有所差別。在資料庫的表中,BLOB實際上是以二進制資料的形式存放的。由于BLOB的特殊性,一般的程式都無法處理它。比如,如果在一張表中存在BLOB,當用access或Database DeskTop(Delphi在帶的資料庫管理工具)等打開它時,BLOB列将隻顯示BLOB字樣。至于該列中實際存的是什麼資料單靠access(Database DeskTop)是無法得到的。如果在我們編制的程式中,用控件打開一個有BLOB字段的表,效果也一樣。我們的程式無法直接顯示、編輯以及插入BLOB字段。可見,如何處理這種不能直接顯示的二進制形式資料,用正常的方法是不能滿足要求的。

結合筆者開發的模型庫管理系統,本文以Visual C++ 6.0和access 2003環境下的資料庫為例,介紹利用ADO實作對超長資料庫字段的通路,包括寫入和讀出。

2 設計資料庫

使用Access2003作為資料庫系統,資料庫名為blob,唯一的一個資料表為blob,如下所示。

其中包括四個字段,分别是id(文本),name(文本),data(OLE對象)和suffix(字尾名),其字段類型必須是OLE對象。id作為主鍵,name是該blob檔案的檔案名,data字段用來儲存二進制大對象,suffix是二進制檔案的字尾名,可以是rm,avi,bmp,mp3等。

3 系統的實作

3.1 SafeArray:

在對BLOB進行操作時,要用到SAFEARRAY結構。SAFEARRAY是一種結構化的資料類型,包含了一個由其它資料類型的資料元素組成的數組。之是以稱之為安全的數組是因為它包含了每一維的邊界資訊,并限制在邊界内進行數組元素的通路。其Win32定義SAFEARRAY如下:

  typedef struct tagSAFEARRAY      
   {      
    unsigned short cDims;      
    unsigned short fFeatures;      
    unsigned long cbElements;      
    unsigned long cLocks;      
    void * pvData;      
    SAFEARRAYBOUND rgsabound[ 1 ];      
   } SAFEARRAY;      

這個結構的成員(cDims,cLocks等)是通過API函數來設定和管理的。真正的資料存放在pvData成員中,而SAFEARRAYBOUND結構定義該數組結構的細節。以下就是該結構成員的簡要描述:

rgsabound是一個有趣的成員,它的結構不太直覺。它是資料範圍的數組。該數組的大小依safe array維數的不同而有所差別。rgsabound成員是一個SAFEARRAYBOUND結構的數組--每個元素代表SAFEARRAY的一個維。 

       
typedef struct tagSAFEARRAYBOUND         
{         
unsigned long cElements;          
unsigned long lLbound;         
} SAFEARRAYBOUND;        

維數被定義在cDims成員中。例如,一個'C'類數組的維數可以是[3][4][5]-一個三維的數組。如果我們使用一個SAFEARRAY來表示這個結構,我們定義一個有三個元素的rgsabound數組--一個代表一維。cDims = 3; SAFEARRAYBOUND rgsabound[ 3 ]; rgsabound[0]元素定義第一維。在這個例子中ILBOUND元素為0,是數組的下界。cElements成員的值等于三。數組的第二維([4])可以被rgsabound結構的第二個元素定義。下界也可以是0,元素的個數是4,第三維也是這樣。

3.2 将二進制檔案寫入到資料庫:

由于這個二進制檔案可能很大,是以無法将所有内容一次性讀入到記憶體。我們需要多次讀入,每次可以使用函數CFile::Read()從檔案中讀出一個資料包,然後調用Field對象的AppandChrnk()函數将該包讀入資料庫。AppandChrnk()函數包含在Field對象中,原型如下:HRESULT AppendChunk (const _variant_t & Data );從函數原型中可以看到關鍵的問題是我們需把二進制資料指派給VARIANT類型的變量。實作的關鍵代碼如下:

while(1)      
     {        uIsRead=f.Read(bVal,ChunkSize);      
         if(uIsRead==0)     break;      
rgsabound[0].cElements =uIsRead; rgsabound[0].lLbound = 0;      
         psa = SafeArrayCreate(VT_UI1,1,rgsabound); ///建立SAFEARRAY對象      
         for(long index=0;index<uIsRead;index++)                
     SafeArrayPutElement(psa,&index,&bVal[index]);      
         varChunk.vt = VT_ARRAY|VT_UI1;      
         varChunk.parray = psa;      
         //加入BLOB類型的資料      
     m_pRecordset->Fields->GetItem("data")->AppendChunk(varChunk);       
         ::VariantClear(&varChunk);      
         ::SafeArrayDestroyData( psa);      
         if(uIsRead<ChunkSize)  break;      
     }      

我們所有的讀入資料工作都在一個while循環中實作,每次讀入一個資料包,直到讀完這個資料,即資料量為二進制資料的長度ChunkSize。其中*pBuf為指向緩沖區指針,即要讀入的資料包。ChunkSize為VARIANT變量,用于儲存二進制資料,psa是指向安全數組SAFEARRAY的指針。

3.3 從資料庫讀出二進制對象到檔案:

同樣,由于這個二進制對象可能很大,無法将所有内容一次性讀入記憶體中對應于儲存資料時我們所使用的AppendChunk函數,讀取資料應該使用GetChunk函數,GetChunk的原型如下:

_variant_t GetChunk (long Length );唯一的參數Length代表需要讀取的位元組數。

實作的關鍵代碼如下:

long lBlobSize = m_pRecordset->Fields-> Item["data"] ->ActualSize;        
     while(lBlobSize>0)      
     {      
         lIsRead= lBlobSize >=ChunkSize? ChunkSize: lBlobSize;      
         //從字段data中擷取一個資料包      
         varChunk = m_pRecordset->Fields->Item["data"]->GetChunk(lIsRead);      
for(index=0;index<lIsRead;index++)               
     ::SafeArrayGetElement(varChunk.parray,&index,buf+index);         
//将資料包寫入檔案      
f.Write(buf,lIsRead);      
lBlobSize -=lIsRead;      
     }      

其中f是一個CFile對象,代表了要存儲的檔案。Long型變量lBlobSize記錄了二進制對象的大小。利用一個while循環,每次從資料庫中讀取lIsRead位元組,直至将其全部讀出。

4 結束語

由于資訊技術的飛速發展,特别是多媒體技術的廣泛運用,可以預見,以後資料庫中大對象的應用将日益普遍,有關大對象的存取勢必将是資料庫技術的一個發展方向。本文讨論了二進制大對象BLOB在資料庫中的存儲與讀取的VC實作,能夠滿足基本的系統要求,具備了一定的可移植性,為今後更廣泛的應用和更深入的研究提供的借鑒基礎