在VC下采用ADO實現BLOB(Binary)數據的存儲,讀取,修改,刪除。
作者:邵盛鬆 2009-09-05
前言
1關於的BLOB(Binary)數據的存儲和讀取功能主要參考了MSDN上的一篇《AppendChunk and GetChunk Methods Example (VC++)》,原文地址是http://msdn.microsoft.com/en-us/library/ms807920.aspx。還有www.vckbase.com上有一篇文章《使用ADO實現BLOB數據的存取 -- ADO開發實踐之二》,原文地址是http://www.vckbase.com/document/viewdoc/?id=252
我在這篇博文當中增加了對BLOB(Binary)數據的存儲,讀取代碼的分析。
2關於BLOB(Binary)數據的修改,我試驗過多種方法,這個問題也花了比較長的時間纔得到解決,原來的方法是從文件讀取的數據,將更改的數據轉化爲CString類型,然後採用SQL的UPDATE語句進行數據的更新。後來在CSDN論壇上得到了vieri_ch的幫助,問題得到了比較好的一種解決方案。
二 實現方法:
1 BLOB(Binary)數據的存儲,將一個文件存儲到數據庫。
存儲部分代碼分析寫的比較詳細,因爲這裏面得一些COM 結構和API需要說明一下
m_pRs爲_RecordsetPtr類型
CFile fileAdd;
if(fileAdd.Open(要存儲文件的路徑,CFile::modeRead)==0) //打開文件
return;
_variant_t varChunk;
long nLength = fileAdd.GetLength();
BYTE* pbuf;
pbuf = new BYTE[nLength];
if(pbuf==NULL)
return;
fileAdd.Read(pbuf,nLength);
BYTE *pBufEx;
pBufEx = pbuf;
SAFEARRAY* psa;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = nLength;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
for (long i = 0; i < nLength; i++)
SafeArrayPutElement(psa,&i, pBufEx++);
VARIANT varBLOB;
varBLOB.vt = VT_ARRAY | VT_UI1;
varBLOB.parray = psa;
m_pRs->ADOFields->GetItem(_variant_t("BLOB類型字段的名稱"))->AppendChunk(varBLOB);
m_pRs->Update();
m_pRs->Close();
在代碼中有一個SAFEARRAY和SAFEARRAYBOUND類型,該類型在頭文件OAIdl.h中定義
typedef struct tagSAFEARRAY
{
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
SAFEARRAYBOUND rgsabound[ 1 ];
}SAFEARRAY;
SAFEARRAY結構體中包含了SAFEARRAYBOUND類型。
先說SAFEARRAYBOUND結構
cElements表示數組中元素的個數。
lLbound表示數組的下界。
代碼中SAFEARRAYBOUND元素個數爲讀取文件的大小,下界是0
操作SAFEARRAY類型是由COM提供了一套API來處理的。oleauto.h 頭文件中可以看到很多關於操作SAFEARRAY數據類型的API.以下少列出幾個
WINOLEAUTAPI SafeArrayAllocDescriptor(UINT cDims, SAFEARRAY ** ppsaOut);
WINOLEAUTAPI SafeArrayAllocDescriptorEx(VARTYPE vt, UINT cDims, SAFEARRAY ** ppsaOut);
WINOLEAUTAPI SafeArrayAllocData(SAFEARRAY * psa);
WINOLEAUTAPI_(SAFEARRAY *) SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND * rgsabound);
SAFEARRAY爲什麼會稱爲安全數組?
從名字上看是安全數組的意思。比如我們第一個數組,數組的大小爲10,數組的下界是從0開始,這時我們訪問下界爲10的元素,這時就發生了錯誤,以爲數組的下界最大爲9。SAFEARRAY這樣定義結構的作用是實現COM API函數可以限制我們訪問下界爲10的元素和其他一些安全操作數組的行爲等
SAFEARRAY結構說明
成員 描述
cDims 數組的維數
fFeatures 標誌
cbElements 數組元素的大小
cLocks 一個計數器,用來跟蹤該數組被鎖定的次數
pvData 指向數據緩衝的指針
標誌表示是在堆上創建還是在棧上創建等還包括其他一些標誌
FADF_AUTO 0x0001
在棧上創建數組
FADF_STATIC 0x0002
在堆上創建數組
FADF_EMBEDDED 0x0004
在結構中創建
FADF_FIXEDSIZE 0x0010
不能改變數組大小
FADF_RECORD 0x0020
記錄容器
FADF_HAVEIID 0x0040
有IID 身份標記 數組
FADF_HAVEVARTYPE 0x0080
VT 類型數組
FADF_BSTR 0x0100
BSTR數組
FADF_UNKNOWN 0x0200
IUnknown* 數組
FADF_DISPATCH 0x0400
IDispatch* 數組
FADF_VARIANT 0x0800
VARIANTs數組
FADF_RESERVED 0xF0E8
餘留,將來使用
COM API函數說明SafeArrayCreate創建數組。參數VT_UI1是表示unsigned int 1字節整數(BYTE)數組
vt是數組類型、lLbound是數組下界值(最小下標)和數組長度
SafeArrayPutElement函數是向一個安全數組中添加一個值,代碼中採用循環一個一個得添加
ADO方法
AppendChunk將數據追加到大型文本、二進制數據 Field 或 Parameter 對象。
語法
object.AppendChunk Data
參數
object Field 或 Parameter 對象
Data 變體型,包含追加到對象中的數據。
2 BLOB(Binary)數據的讀取
將數據庫中的數據讀出並寫入文件。
long lDataLength = m_pRs->GetFields()->GetItem(_variant_t("BLOB類型的字段"))->ActualSize;
if (lDataLength>0)
{
_variant_t varBLOB;
varBLOB=m_pRs->GetFields()->GetItem(_variant_t("BLOB類型的字段"))->GetChunk(lDataLength);
if(varBLOB.vt== (VT_ARRAY|VT_UI1) && varBLOB.vt!=VT_EMPTY && varBLOB.vt!=VT_NULL )
{
BYTE *pBuf = NULL;
pBuf = (BYTE*)GlobalAlloc(GMEM_FIXED,lDataLength);
SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);
strFileName表示生成包含文件名的路徑
CFile outFile(strFileName,CFile::modeCreate|CFile::modeWrite); //構造新文件,如果文件存在,則長度變爲0
outFile.Write(pBuf,lDataLength);
outFile.Close();
SafeArrayUnaccessData (varBLOB.parray);
}
}
m_pRs->Close();
3 BLOB(Binary)數據的修改
CFileException eFile;
CFile fileSave;
strPath表示需要修改文件的路徑
fileSave.Open(strPath,CFile::modeReadWrite|CFile::shareDenyWrite|CFile::shareDenyRead|CFile::typeBinary,&eFile);
_variant_t varChunk;
long nLength = fileSave.GetLength();
BYTE* pbuf;
pbuf = new BYTE[nLength];
if(pbuf == NULL) return;
fileSave.Read(pbuf,nLength);
fileSave.Close();
BYTE *pBufEx;
pBufEx = pbuf;
SAFEARRAY* psa;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = nLength;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
for (long i = 0; i < nLength; i++)
SafeArrayPutElement(psa,&i, pBufEx++);
VARIANT varBLOB;
varBLOB.vt = VT_ARRAY | VT_UI1;
varBLOB.parray = psa;
CString strSQL;
strSQL.Format(_T("UPDATE 表名 SET BLOB字段名=? WHERE ID='1'");
m_pCom.CreateInstance(__uuidof(Command));
m_pCom->ActiveConnection = m_pCon;
m_pCom->CommandText = _bstr_t(strSQL);
m_pCom->CommandType = adCmdText;
m_pCom->Parameters->Append(m_pCom->CreateParameter(_T("@參數名"),adVarBinary, adParamInput, -1, varBLOB));
m_pCom->Execute(NULL,NULL,adCmdText);
4 BLOB(Binary)數據的修改
刪除比較簡單一點,同時表定義該字段允許爲空
執行一個SQL語句就可以了
UPDATE 表名 SET BLOB字段名 = NULL WHERE ID=1
以上程序已經在Visual C++2005 Unicode下調試通過