VC用ADO存取顯示jpg/bmp點陣圖檔案
週六和今天兩天的時間,把資料庫關於圖片的儲存和顯示 實現了,雖然時間有點長,但是還是實現了。以下是網上找到的資料,很有用。
第一步:首先是要開啟一個位圖檔案,這裡使用的控制元件用Picture控制元件,就是控制元件圖示右邊最上面那個,改ID號為IDC_PICTURE,然後定義兩個成員變數
char *m_char;//圖片檔案指標
DWORD m_nFileLen;//圖片長度
然後在函式中寫入:
CFileDialog dlg(TRUE,NULL,NULL,0,"photo Files (*.jpg;*.bmp)|*.jpg;*.bmp|",this);
if(IDOK==dlg.DoModal())
{
m_path=dlg.GetPathName();
m_bool=true;
}
CWnd *pWnd = GetDlgItem(IDC_PICTURE);
CRect rect;
pWnd->GetClientRect(&rect);
CDC *pDC = pWnd->GetDC();
CFileStatus fstatus;
CFile file;
LONG cb;
BOOL m_tm=false;
IPicture *pPic;
CString m_sPath;
if (file.Open(m_path,CFile::modeRead)&& file.GetStatus(m_path,fstatus)&&((cb = fstatus.m_size) != -1))
{
if(cb>1048576) //可在此設定圖片大小
{
m_tm=false;
MessageBox("圖片不能大於1M","提示!");
}
else
{
m_nFileLen=(UINT)cb;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, cb);
LPVOID pvData = NULL;
if (hGlobal != NULL)
{
if ((pvData = GlobalLock(hGlobal)) != NULL)
{
file.ReadHuge(pvData, cb);
m_char=(char*)pvData;
GlobalUnlock(hGlobal);
CreateStreamOnHGlobal(hGlobal, TRUE, &pStm);
m_tm=true;
// m_bool=true;
}
else
AfxMessageBox("不是圖片檔案!");
}
else
AfxMessageBox("申請記憶體失敗!");
}
}
else
AfxMessageBox("不是圖片檔案!");
if(m_tm)
{
SUCCEEDED(OleLoadPicture(pStm,fstatus.m_size,TRUE,IID_IPicture,(LPVOID*)&pPic));
OLE_XSIZE_HIMETRIC hmWidth;
OLE_YSIZE_HIMETRIC hmHeight;
pPic->get_Width(&hmWidth);
pPic->get_Height(&hmHeight);
if(FAILED(pPic->Render(*pDC,0,0,rect.Width(),rect.Height(),0,hmHeight,hmWidth,-hmHeight,NULL)))
AfxMessageBox("渲染影象失敗!");
pPic->Release();
}
這裡還有個小小的問題,就是當窗口出現重畫的時候圖片就會消失,可以把上面的程式碼加入到OnPaint函式中去;
接來就是儲存檔案(怎麼訪問資料庫的可以去看下我空間裡的"用ADO訪問資料庫"):
在Access資料庫中把要放相片欄位的型別改為OLE 型別(在SQL資料庫改成相片型別).
m_RecordSet->AddNew();
char *pBuf = m_char;//把圖片的指標傳給pBuf
VARIANT varBLOB;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];
if(pBuf)
{
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = m_nFileLen;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
for (long i = 0; i < (long)m_nFileLen; i++)
SafeArrayPutElement (psa, &i, pBuf++);
varBLOB.vt = VT_ARRAY | VT_UI1;
varBLOB.parray = psa;
m_pRecordset->GetFields()->GetItem("讀者相片")->AppendChunk(varBLOB);
}
m_pRecordset->Update();
接下來就是把圖片從資料庫中取出來,在放圖相的對話方塊中加入Picture控制元件,改ID為IDC_PICTURE,然後 在函式中寫入:
IStream *pStm;
long lDataSize = m_pRecordset->GetFields()->GetItem("讀者相片")->ActualSize;
if(lDataSize > 0)
{
_variant_t varBLOB;
varBLOB = theApp.m_data.m_pRecordset->GetFields()->GetItem("讀者相片")->GetChunk(lDataSize);
if(varBLOB.vt == (VT_ARRAY | VT_UI1))
{
char *pBuf = NULL;
SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, lDataSize);
LPVOID pvData = NULL;
if (hGlobal != NULL)
{
if ((pvData = GlobalLock(hGlobal)) != NULL)
{
memcpy(pvData,pBuf,lDataSize);
SafeArrayUnaccessData (varBLOB.parray);
GlobalUnlock(hGlobal);
CreateStreamOnHGlobal(hGlobal, TRUE, &pStm);
}
else
AfxMessageBox("載入圖片失敗!");
}
else
AfxMessageBox("申請記憶體失敗!");
}
CWnd *pWnd = GetDlgItem(IDC_PICTURE);
CRect rect;
pWnd->GetClientRect(&rect);
CDC *pDC = pWnd->GetDC();
IPicture *pPic;
if(SUCCEEDED(OleLoadPicture(pStm,lDataSize,TRUE,IID_IPicture,(LPVOID*)&pPic)))
{
OLE_XSIZE_HIMETRIC hmWidth;
OLE_YSIZE_HIMETRIC hmHeight;
pPic->get_Width(&hmWidth);
pPic->get_Height(&hmHeight);
if(FAILED(pPic->Render(*pDC,0,0,rect.Width(),rect.Height(),0,hmHeight,hmWidth,-hmHeight,NULL)))
AfxMessageBox("渲染影象失敗!");
pPic->Release();
}
}
這樣寫也會出現當視窗重繪視窗圖片就會消失的問題,可以把上面的函式寫在OnPaint()中.整個顯示,存取和訪問圖片的程式就已經完成了.
CoInitialize(NULL);
_ConnectionPtr m_pConnect;
try
{
// 建立Connection物件
m_pConnect.CreateInstance("ADODB.Connection");
// 設定連線字串,必須是BSTR型或者_bstr_t型別
_bstr_t strConnect = "Provider=SQLOLEDB.1;Password=111111;Persist Security Info=True;User ID=sa;Initial Catalog=Picture;Data Source=SHOWFLY\\SQL2005";
m_pConnect->Open(strConnect,"","",adModeUnknown);
}
// 捕捉異常
catch(_com_error e)
{
// 顯示錯誤資訊
AfxMessageBox(e.Description());
}
CFile fileAdd;
if(fileAdd.Open("F:/20087610203.JPG",CFile::modeRead)==0) //開啟檔案
return;
_variant_t varChunk;
long m_nFileLen = fileAdd.GetLength();
BYTE* m_pBMPBuffer;
m_pBMPBuffer = new BYTE[m_nFileLen];
if(m_pBMPBuffer==NULL)
return;
fileAdd.Read(m_pBMPBuffer,m_nFileLen);
//向資料庫新增圖片
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset));
try{
m_pRecordset->Open(_variant_t("dbo.userphoto"),_variant_t((IDispatch*)m_pConnect,true),adOpenKeyset,adLockOptimistic,adCmdTable);
}
catch(_com_error &e)
{
::MessageBox(NULL,"無法開啟userphoto表!","提示",MB_OK|MB_ICONWARNING);
}
char *pBuf = (char*)m_pBMPBuffer;
VARIANT varBLOB;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];
m_pRecordset->AddNew(); ///新增新記錄
m_pRecordset->PutCollect("username",_variant_t("小李")); ///為新記錄填充username欄位
m_pRecordset->PutCollect("old",_variant_t((long)28)); ///填充old欄位
if(pBuf)
{
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = m_nFileLen;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound); ///建立SAFEARRAY物件
for (long i = 0; i < (long)m_nFileLen; i++)
SafeArrayPutElement (psa, &i, pBuf++); ///將pBuf指向的二進位制資料儲存到SAFEARRAY物件psa中
varBLOB.vt = VT_ARRAY | VT_UI1; ///將varBLOB的型別設定為BYTE型別的陣列
varBLOB.parray = psa; ///為varBLOB變數賦值
m_pRecordset->GetFields()->GetItem("photo")->AppendChunk(varBLOB);///加入BLOB型別的資料
}
m_pRecordset->Update(); ///儲存我們的資料到庫中
//從資料庫讀出圖片
long lDataLength = m_pRecordset->GetFields()->GetItem(_variant_t("photo"))->ActualSize;
if (lDataLength>0)
{
_variant_t varBLOB;
varBLOB=m_pRecordset->GetFields()->GetItem(_variant_t("photo"))->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);
CFile outFile("D:/20087610203.bmp",CFile::modeCreate|CFile::modeWrite); //構造新檔案,如果檔案存在,則長度變為0
outFile.Write(pBuf,lDataLength);
outFile.Close();
SafeArrayUnaccessData (varBLOB.parray);
}
}
m_pRecordset->Close();
m_pConnect->Close();
::CoUninitialize();