1 引言 在許多項目開發中,由于存在著大量的數據需要存儲,通常采用數據庫來存儲這些數據,使得數據庫技術被越來越廣泛地應用。在這些數據中有簡單的文本文件,只需通過api的簡單編程即可實現,但也存在許多大對象數據,比如圖像、音頻和視頻等,其存儲形式有所不同。作為大對象數據的圖像數據,一般以blob(二進制大對象,binary large object)形式存儲,例如access數據庫中ole對象數據類型、sql server數據庫中的image數據類型等。在vc++ 7.0的環境下,為了高效快捷的訪問圖像數據,需要采用合適的數據庫訪問技術。 在vc++中標準的訪問數據庫技術有很多種,根據開發的項目,筆者采用了數據庫技術中最常見有效的兩種技術ado技術和mfc odbc在access數據庫中訪問圖像數據。 2 圖像數據訪問原理 雖然圖像數據的格式有多種,例如bmp、jpg和gif等格式,但都可以看成是二進制流,即blob類型。blob類型數據需要占用較大的硬盤空間和內存,對存儲效率和查詢速度都有很大的影響。 圖像數據的訪問分存儲和讀取兩個過程。在存儲過程中,由于存放圖像數據的字段是可變長度(長度一般為0~2g)的blob類型,無法直接存儲,必須首先將存儲在文件中的圖像數據以二進制流的形式讀到緩沖區中,然后再將緩沖區中的圖像數據添加到access數據庫中的ole對象中。在讀取過程中,將存放在access數據庫中的圖像數據讀到緩沖區中,然后在顯示在用戶界面上。圖像數據的訪問原理如圖1所示。 圖1 圖像數據訪問原理圖 3 應用ado訪問圖像數據3.1 ado的介紹 ado(activex data objects)是microsoft數據庫應用程序開發的接口,是建立在ole db技術之上的高層數據庫訪技術,簡化了編程,且有利于程序的可移植性及可擴充性。 ado包括connection對象、command對象、parameter對象、recordset對象、field對象、error對象、property對象以及相應的集合對象,這些對象被封裝在_connection -ptr接口、_commndptr接口和_recordsetptr接口這三個基本接口中。 3.2 ado連接數據庫 (1) 導入ado庫 使用ado前需要在stdafx.h頭文件中導入該庫,只需利用import指令將此動態鏈接庫導入,具體代碼如下[1]: #import“c:\program files\common files \system\ado\msado15.dll”\no_namespace\rename(“eof”, “adoeof”) (2) 初始化com庫 ado本身是一個com組件,在使用時需要初始化com庫,需要調用coinitialize函數來實現。 :: coinitialize(null); (3) 創建connection對象并連接數據庫 創建connection對象: _connectionptr m_pconnection; m_pconnection.createinstanc -e(“adodb.connection”); 連接數據庫: _pconnection-》open(“provide -r=microsoft.jet.oledb.4.0;data source = picture.mdb”, “”,“”, admodeunknown); (4) 執行操作 m_precordset.createinstance(“adodb.recordset”); m_precordset-》open ((_variant_t)strsql,_variant_t((idispatch*) theapp.m_pconnection,true),adopendynamic,adlockoptimistic,adcmdtext); 3.3 圖像數據的存儲 對于一般數據,可以通過recordset對象的getcollect()和putcollect()函數來對數據進行存儲和讀取,而對于圖像數據,就需要使用field對象中的appendchunk函數來進行存儲和getchunk函數來進行讀取。圖像數據的存儲過程可以分為以下三步: (1) 打開圖像數據,獲得數據長度;申請緩沖區,把文件讀入此緩沖區。 (2) 構造一個一維數組,把緩沖區中的數據拷貝到數組中,然后構造一個變體對象,用數組來對此對象賦值。 (3) 調用appendchunk函數,把數據存入recordset對象的本地緩沖區。 具體實現代碼如下: if(m_pic.m_ipicture != null) m_pic.freepicturedata(); if(f.open(m_strphotopath, cfile::moderead | cfile::typebinary, &e)) //打開了一個圖像文件 {nsize = f.getlength();//先得到圖像數據長度 byte * pbuffer = new byte [nsize]; //根據數據的長度申請緩沖區 if (f.read(pbuffer, nsize) 》 0 ) //把圖像數據讀到緩沖區 {byte *pbuf = pbuffer; //把pbuffer里的jpg數據放到庫中 variant varblob; safearray *psa; safearraybound rgsabound[1]; //創建safearray對象 if(pbuf) {rgsabound[0].llbound = 0; rgsabound[0].celements = nsize; psa = safearraycreate(vt_ui1, 1, rgsabound); for (long i = 0; i 《 (long)nsize; i++) safearrayputelement (psa, &i, pbuf++); varblob.vt = vt_array | vt_ui1; varblob.parray = psa; m_precordset-》getfields()-》getitem(“picture”)-》appendchunk(varblob); m_precordset-》update();//更新數據庫 } delete [] pbuffer; //釋放緩沖區 pbuf=0; } f.close(); } 3.4 圖像數據的讀取 圖像數據的讀取過程可以分為以下三歩: (1) 獲得picture字段中圖像文件的實際長度。 (2) 調用getchunk函數,返回變體對象。 (3) 獲得數組數據指針,把數據寫入打開的文件中。 具體實現代碼如下: long nsize=m_precordset-》getfields()-》getitem (“picture ”)-》actualsize;//獲得圖像文件的長度 if(nsize1 》 0) { varblob=m_precordset-》getfields()-》getitem (“picture ”)-》getchunk(nsize); //獲得varblob的值 if(varblob.vt == (vt_array | vt_ui1)) {char *pbuf = null; lpvoid pbuf2 = null; safearrayaccessdata(varblob.parray, (void **)&pbuf);//獲得數組數據指針 hglobal=globalalloc(gmem_moveable, nsize); pbuf2 = globallock(hglobal); memcpy(pbuf2,pbuf,nsize1); //復制數據到緩沖區 ::globalunlock(hglobal); safearrayunaccessdata (varblob.parray); hr=::createstreamonhglobal( hglobal, true, &pstream ); hr=::oleloadpicture( pstream, nsize1, true, iid_ipicture, ( lpvoid * )&ppicture ); ppicture-》render(pdc-》m_hdc,0,0,rect.width(),rect.height(), 0,nheight,nwidth,-nheight,null); //將圖像數據顯示在用戶界面 } } 4 應用mfc odbc訪問圖像數據 4.1 mfc odbc的介紹 odbc(open database connect -ivity)是微軟公司開放服務結構中有關數據庫的一個組成部分。mfc的odbc類對較復雜的odbc api進行了封裝,提供了簡化的調用接口,方便了數據庫應用程序的開發[2]。 mfc odbc類主要包括cdatabase類、crecordset類和cfieldexchange類。cdatabase類的主要功能是建立與數據庫的連接;crecordset類針對數據源中記錄集,它負責對記錄的操作;cfieldexchange類負責crecordset與數據源的數據交換。 4.2 配置odbc數據源 用odbc管理器定義了一個數據源,管理器根據數據源所在的位置,數據庫類型及odbc驅動器等信息,建立起odbc與具體數據庫之間的聯系。在windows xp的系統中,配置odbc數據源的過程為:控制面板—性能和維護—管理工具—數據源(odbc)。 4.3 odbc連接數據庫 創建cdatabase對象并連接數據庫: cdatabase m_db;//定義的數據庫全局變量 cbitmap bitmap; //定義的圖像類全局變量,存儲圖像數據 m_db.open(null, false, false, “odbc;driver={microsoft access driver (*.mdb)}; dbq= picture.mdb”); 4.4 圖像數據的存儲 首先做一些變量的定義: m_ picture為picture字段對應的長二進制成員變量; m_hdata用來存放picture字段的數據; m_dwdatalength為picture字段的實際長度。 圖像數據的存儲過程可以分為以下三步: (1) 打開圖像數據,獲得數據長度; (2) 申請緩沖區,把文件讀入此緩沖區; (3) 通過調用update()刷新數據庫記錄。 具體實現代碼如下: static char based_code szfilter[]=“bitmap files (*.bmp)|*.bmp||”; cdbimages dbimages(m_db); cfiledialog fd(true,null,null,0,szfilter,this); if (idok != fd.domodal()) return; dbimages.open(); dbimages.addnew(); cfile fileimage; cfilestatus filestatus; fileimage.open(fd.getpathname(), cfile::moderead); fileimage.getstatus(filestatus); //獲得打開圖像文件的狀態 dbimages.m_ picture.m_dwdatal -ength = filestatus.m_size; hglobal hglobal=globalalloc(gptr,filestatus.m_size); //分配內存 dbimages.m_ picture.m_hdata = globallock(hglobal); //鎖定內存 fileimage.readhuge(dbimages.m_ picture.m_hdata,filestatus.m_size); //向緩沖區讀取圖像數據 dbimages.setfielddirty(&dbimages.m_ picture); //修改標志數據庫 dbimages.setfieldnull(&dbim -ages.m_ picture,false); dbimages.update(); //刷新數據庫的記錄 globalunlock(hglobal); dbimages.close(); 4.5 圖像數據的讀取 圖像數據的讀取過程可以分為以下三步: (1) 定義臨時文件。 (2) 向臨時文件中存放從數據庫中讀取到的圖像數據。 (3) 取出圖像數據,獲得圖像數據的寬度和高度。 具體實現代碼如下: cdbimages dbimages(m_db); cstring strfilename ; i=1; strfilename.format(“%s”,i) dbimages.open(); if (dbimages.iseof()) afxmessagebox(“unable to get image from picture”); else{char tmppath[_max_path 1]; gettemppath(_max_path,tmppath); strfilename.insert(0,tmppath); cfile outfile(strfilename,cfile::modecreate |cfile::modewrite); lpstr buffer=(lpstr)globallock(dbimages.m_ picture.m_hdata); outfile.writehuge(buffer,dbima -ges.m_ picture.m_dwdatalength); globalunlock(dbimages.m_ picture.m_hdata); outfile.close(); hbitmap hbm=(hbitmap)::loa -dimage(null, strfilename,image_bitmap, 0, 0, lr_loadfromfile); if (bitmap.attach(hbm)) { bitmap bm; bitmap.getbitmap(&bm); //獲得圖像的尺寸 bitmap.setbitmapdimension(bm.bmwidth,bm.bmheight);//獲得圖像的寬度和高度 } 5 結束語 筆者介紹了使用ado技術和mfc odbc在access中存儲和讀取圖像數據的方法以及部分程序代碼。這兩種訪問方法在vc++ 7.0的環境下進行調試結果顯示,使用ado來訪問數據庫中的圖像數據更加的方便,更加的高效,使用mfc odbc方法訪問大對象數據的速度相對較慢。在開發擴展時會發現mfc odbc不能用于其他的非關系數據庫,使用的范圍相對較窄。這兩種方法還可以應用于其他二進制大對象數據的訪問。 電子工程網 |