用VC實現(xiàn)對超長數(shù)據(jù)庫字段的操作

2010-08-28 10:49:52來源:西部e網(wǎng)作者:

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

  結合筆者開發(fā)的模型庫管理系統(tǒng),本文以Visual C++ 6.0和access 2003環(huán)境下的數(shù)據(jù)庫為例,介紹利用ADO實現(xiàn)對超長數(shù)據(jù)庫字段的訪問,包括寫入和讀出。

  設計數(shù)據(jù)庫

  使用Access2003作為數(shù)據(jù)庫系統(tǒng),數(shù)據(jù)庫名為blob,唯一的一個數(shù)據(jù)表為blob,如圖1所示。

  \
  圖1

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

  系統(tǒng)的實現(xiàn):

  1、SafeArray:

  在對BLOB進行操作時,要用到SAFEARRAY結構。SAFEARRAY是一種結構化的數(shù)據(jù)類型,包含了一個由其它數(shù)據(jù)類型的數(shù)據(jù)元素組成的數(shù)組。之所以稱之為安全的數(shù)組是因為它包含了每一維的邊界信息,并限制在邊界內(nèi)進行數(shù)組元素的訪問。其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函數(shù)來設置和管理的。真正的數(shù)據(jù)存放在pvData成員中,而SAFEARRAYBOUND結構定義該數(shù)組結構的細節(jié)。以下就是該結構成員的簡要描述:

成員 描述
cDims 數(shù)組的維數(shù)
fFeatures 用來描述數(shù)組如何分配和如何被釋放的標志
cbElements 數(shù)組元素的大小
cLocks 一個計數(shù)器,用來跟蹤該數(shù)組被鎖定的次數(shù)
pvData 指向數(shù)據(jù)緩沖的指針
rgsabound 描述數(shù)組每維的數(shù)組結構,其大小是可變的

  rgsabound是一個有趣的成員,它的結構不太直觀。它是數(shù)據(jù)范圍的數(shù)組。該數(shù)組的大小依safe array維數(shù)的不同而有所區(qū)別。rgsabound成員是一個SAFEARRAYBOUND結構的數(shù)組--每個元素代表SAFEARRAY的一個維。

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


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


    2、將二進制文件寫入到數(shù)據(jù)庫:

  由于這個二進制文件可能很大,所以無法將所有內(nèi)容一次性讀入到內(nèi)存。我們需要多次讀入,每次可以使用函數(shù)CFile::Read()從文件中讀出一個數(shù)據(jù)包,然后調(diào)用Field對象的AppandChrnk()函數(shù)將該包讀入數(shù)據(jù)庫。AppandChrnk()函數(shù)包含在Field對象中,原型如下:HRESULT AppendChunk (const _variant_t & Data );從函數(shù)原型中可以看到關鍵的問題是我們需把二進制數(shù)據(jù)賦值給VARIANT類型的變量。實現(xiàn)的關鍵代碼如下:

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); ///創(chuàng)建SAFEARRAY對象
 for(long index=0;index<UISREAD;INDEX++)
  SafeArrayPutElement(psa,&index,&bVal[index]);
  varChunk.vt = VT_ARRAY|VT_UI1;
  varChunk.parray = psa;
  //加入BLOB類型的數(shù)據(jù)
  m_pRecordset->Fields->GetItem("data")->AppendChunk(varChunk);
  ::VariantClear(&varChunk);
  ::SafeArrayDestroyData( psa);
  if(uIsRead<<CHUNKSIZE) break;
}

  我們所有的讀入數(shù)據(jù)工作都在一個while循環(huán)中實現(xiàn),每次讀入一個數(shù)據(jù)包,直到讀完這個數(shù)據(jù),即數(shù)據(jù)量為二進制數(shù)據(jù)的長度ChunkSize。其中*pBuf為指向緩沖區(qū)指針,即要讀入的數(shù)據(jù)包。ChunkSize為VARIANT變量,用于保存二進制數(shù)據(jù),psa是指向安全數(shù)組SAFEARRAY的指針。

  3、從數(shù)據(jù)庫讀出二進制對象到文件:

  同樣,由于這個二進制對象可能很大,無法將所有內(nèi)容一次性讀入內(nèi)存中對應于保存數(shù)據(jù)時我們所使用的AppendChunk函數(shù),讀取數(shù)據(jù)應該使用GetChunk函數(shù),GetChunk的原型如下:

  _variant_t GetChunk (long Length );唯一的參數(shù)Length代表需要讀取的字節(jié)數(shù)。

  實現(xiàn)的關鍵代碼如下:

long lBlobSize = m_pRecordset->Fields-> Item["data"] ->ActualSize;
while(lBlobSize>0)
{
 lIsRead= lBlobSize >=ChunkSize? ChunkSize: lBlobSize;
 //從字段data中獲取一個數(shù)據(jù)包
 varChunk = m_pRecordset->Fields->Item["data"]->GetChunk(lIsRead);
 for(index=0;index<LISREAD;INDEX++)
  ::SafeArrayGetElement(varChunk.parray,&index,buf+index);
  //將數(shù)據(jù)包寫入文件
  f.Write(buf,lIsRead);
  lBlobSize -=lIsRead;
}

  其中f是一個CFile對象,代表了要存儲的文件。Long型變量lBlobSize記錄了二進制對象的大小。利用一個while循環(huán),每次從數(shù)據(jù)庫中讀取lIsRead字節(jié),直至將其全部讀出。

  結束語

  由于信息技術的飛速發(fā)展,特別是多媒體技術的廣泛運用,可以預見,以后數(shù)據(jù)庫中大對象的應用將日益普遍,有關大對象的存取勢必將是數(shù)據(jù)庫技術的一個發(fā)展方向。本文討論了二進制大對象BLOB在數(shù)據(jù)庫中的存儲與讀取的VC實現(xiàn),能夠滿足基本的系統(tǒng)要求,具備了一定的可移植性,為今后更廣泛的應用和更深入的研究提供的借鑒基礎。

關鍵詞:VC

贊助商鏈接: