C++使用ADO存取图片
一、使用數據庫前的準備
我們使用ADO,是用_ConnectionPtr,_RecordsetPtr來操縱數據庫的。還有一個_CommandPtr,本程序沒有使用它。
為了使用ADO,需要導入ADO動態鏈接庫。在工程的stdafx.h文件中,添加如下代碼:
//導入ADO
#import "C:\Program Files\Common Files\System\ado\msado15.dll"\
??? rename_namespace("ADOCG")rename("EOF","EndOfFile")
using namespace ADOCG;
這些代碼聲明,在這個工程中使用ADO但不使用ADO的名字空間,并且為了避免常數沖突,將常數EOF改名為adoEOF。
再有就是要建一個簡單的數據庫,名字叫TestImage,里面有一個表Images,這個表有三個字段,分別是ID,Name,ImageData。
二、連接數據庫
連接數據庫的代碼可以放入一個函數中,在想調用的地方調用。一般不推薦在CAPP類的Initalize()里連接數據庫,在退出程序時關閉數據庫連接。應該是在使用時連接,使用完馬上關閉。項目中m_pConn是_ConnectionPtr類型的變量。
BOOL OpenConnection()
{
?? if(m_pConn == NULL)
?? {
????? m_pConn.CreateInstance("ADODB.Connection"); //創建_ConnectionPtr的一個實例
?? }
?? try
?? {
????? if(adStateClosed == m_pConn->State) //如果已關閉
????? {
???????? m_pConn->Open("driver={SQL Server};Server=HP-CADD722B76A0;DATABASE=TestImage;UID=sa;PWD=sa","","",adModeUnknown);???? //因數據庫而異
???????? return true;
????? }
?? }
?? catch(_com_error e)
?? {
???? AfxMessageBox(_T("連接數據庫失敗!"));
????? return false;
?? }
}
三、打開數據集,操縱數據庫
在使用_RecordSetPtr對象m_pRecord時,必須先創建這種對象的一個實例:
m_pRecord.CreateInstance( __uuidof(RecordSet) );
CString strSQL;
//獲取表中最大的id,下一次插入時就用id+1
strSQL.Format(_T("Select count(*) as num, Max(ID) as maxid from Images"));
try
{
?????? m_pRecord->Open(strSQL.AllocSysString(), m_pConn.GetInterfacePtr(),
?????????? adOpenDynamic, adLockUnspecified, adCmdText);
}
catch (_com_error e)
{
?????? AfxMessageBox(_T("讀取最大的id異常"));
?????? eturn;
}
//從RecordSet中獲取數據數目和當前數據庫中最大的ID。
int num = m_pRecord->GetCollect("num");
int maxid;
if (num != 0)
{
?????? maxid = m_pRecord->GetCollect("maxid");
}
else
{
?????? maxid = 0;
}
strSQL.Format(_T("Select * from Images where ID = %d"), maxid);
//下面向數據庫中插入圖片等。
//首先從數據庫中讀id最大的那條數據,主要目的是為了將RecordSet初始化
m_pRecord.CreateInstance(__uuidof(Recordset));
上面這句一定要注意,因為上一次把一些數據放入m_pRecord中,這一次再放的時候,要重新創建一次,否則數據格式要么不匹配,要么保留有上一次的數據,定位困難。
m_pRecord->Open(strSQL.AllocSysString(), m_pConn.GetInterfacePtr(),?
????? adOpenDynamic, adLockOptimistic, adCmdText);???? //這是AddNew方法要求的
CString imagepath = _T("F:/200713454/20090326.bmp");
CString imagename = imagepath.Right(12);
try
{
?????? m_pRecord->AddNew();????? //為記錄集添加新的一行,更新時就會把這條新紀錄放到數據庫中
}
catch (_com_error e)?
{
????? AfxMessageBox(_T("不能插入一條新的記錄"));
?????? return;
}
try
{
????? //使用putcollect插入非圖像數據,使用SetImage2DB插入圖像數據
?????? m_pRecord->PutCollect("ID", _variant_t(maxid+1));
?????? m_pRecord->PutCollect("Name", _variant_t(imagename));
?????? SetImage2DB(imagepath);
}
catch (_com_error e)
{
?????? AfxMessageBox(_T("插入圖片有異常"));
?????? return;
}
m_pRecord->Update();
//使用完畢,關閉m_pRecord,并設置為NULL,最后關閉數據庫連接
m_pRecord->Close();
m_pRecord = NULL;
CloseConnection();
四、讀取圖片并存儲到本地計算機
要將數據庫中的二進制數據變為圖片,最簡單的方法就是用GDI+。GDI+有一個類是Image,可以用stream來創建對象,還可以用Save方法保存到本地,所以這個類很符合需要。
要使用GDI+,需要做些設置。首先在VS2005的項目屬性中,加上gdiplus.lib。
然后在stdafx.h中添加代碼
#include <GdiPlus.h>
using namespace Gdiplus;
在CApp類添加兩個變量:
GdiplusStartupInput m_gdiplusstartUpInput;
ULONG_PTR m_GdiplusToken;
在CApp的InitInstance函數中添加GdiplusStartup(&m_GdiplusToken, &m_gdiplusstartUpInput, NULL);在ExitInstance函數中添加GdiplusShutdown(m_GdiplusToken);
以下是讀取圖片數據并保存到本地的代碼實現:
OpenConnection();
m_pRecord.CreateInstance(__uuidof(Recordset));
CString strSQL;
strSQL.Format(_T("Select * from Images where ID = 1"));
try
{
????? m_pRecord->Open(strSQL.AllocSysString(), m_pConn.GetInterfacePtr(),
????????? adOpenDynamic, adLockOptimistic, adCmdText);
}
catch (_com_error e)
{
????? AfxMessageBox(_T("讀取圖片信息異常"));
???? return;
}
LPVOID Data;
char* pbuf = NULL;
long lDatasize = m_pRecord->GetFields()->GetItem("ImageData")->ActualSize; //數據庫中圖像數據長度
CString imagename = m_pRecord->GetCollect("Name").bstrVal;
if (lDatasize > 0)
{
????? _variant_t varBLOB;
????? varBLOB = m_pRecord->GetFields()->GetItem("ImageData")->GetChunk(lDatasize);
????? Data = new char[lDatasize+1];
????? if (varBLOB.vt == (VT_ARRAY|VT_UI1))
????? {
????????? SafeArrayAccessData(varBLOB.parray, (void **)&pbuf);
????????? memcpy(Data, pbuf, lDatasize);
????????? SafeArrayUnaccessData(varBLOB.parray);
???? }
}
IStream* pStm;
LONGLONG cb = lDatasize;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, cb);
LPVOID pvData;
if (hGlobal != NULL)
{
????? pvData = GlobalLock(hGlobal);
????? memcpy(pvData, Data, cb);
????? GlobalUnlock(hGlobal);
????? CreateStreamOnHGlobal(hGlobal, TRUE, &pStm);
}
else
{
????? AfxMessageBox(_T("Error"));
????? return;
}
CLSID encoderClsid;
GetEncoderClsid(L"image/bmp",&encoderClsid);???? //確定編碼格式是bmp格式
Image image(pStm, TRUE);
CString imagepath;
imagepath.Format(_T("F:/200713454/%s"), imagename);
image.Save(imagepath, &encoderClsid, NULL);????? //把image中的數據按照bmp編碼格式存到本地
m_pRecord->Close();
m_pRecord = NULL;
CloseConnection();
上面存儲和讀取數據的代碼中用到了兩個函數,GetEncoderClsid和SetImage2DB。它們的實現如下:
這個函數和上面的存/取函數都是一個類的成員函數,而m_pConn和m_pRecord是這個類的成員變量,所以
void CDlgTest::SetImage2DB(CString path)
{
VARIANT varChunk;
SAFEARRAY* psa;
SAFEARRAYBOUND rgsabound[1];
CFile f(path.operator LPCTSTR(),CFile::modeRead);
BYTE bval[ChunkSize+1];
long uIsRead=0;
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);
????? for (long index=0;index<uIsRead;index++)
????? {
????????? if (FAILED(SafeArrayPutElement(psa,&index,&bval[index])))
????????? AfxMessageBox(_T("錯誤。"));
????? }
????? varChunk.vt =VT_ARRAY|VT_UI1;
????? varChunk.parray=psa;
???? try
????? {
????????? m_pRecord->Fields->GetItem("ImageData")->AppendChunk(varChunk);
????? }
????? catch (_com_error e)
????? {
????????? AfxMessageBox(_T("錯誤。"));
????? }
????? ::VariantClear(&varChunk);
????? ::SafeArrayDestroyData(psa);
????? if (uIsRead<ChunkSize)break;
??? }
??? f.Close();
}
INT CDlgTest::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
??? UINT num = 0; // number of image encoders
??? UINT size = 0; // size of the image encoder array in bytes
??? ImageCodecInfo* pImageCodecInfo = NULL;
??? GetImageEncodersSize(&num, &size);
??? if(size == 0)
??????? return -1; // Failure
??? pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
??? if(pImageCodecInfo == NULL)
??????? return -1; // Failure
??? GetImageEncoders(num, size, pImageCodecInfo);
??? for(UINT j = 0; j < num; ++j)
??? {
??????? if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
??????? {
??????????? *pClsid = pImageCodecInfo[j].Clsid;
??????????? free(pImageCodecInfo);
??????????? return j; // Success
???????? }?
??? }
??? free(pImageCodecInfo);
??? return -1; // Failure
}
這樣就實現了存儲圖片和從數據庫中把圖片下載到本地。
總結
以上是生活随笔為你收集整理的C++使用ADO存取图片的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: _variant_t和_bstr_t有什
- 下一篇: c++对象工厂