C++使用ADO实现存取图片的方法

2025-05-27 0 93

一般在网上查到的资料中向Server2000存储图片代码比较多,从数据库中读取图片并显示也不少,但是把图片从数据库中二进制数据转换为原图片保存在本地,就很少有C++代码了。本文就此问题一步一步地讲一讲解决的方法:

一、使用数据库前的准备

我们使用ADO,是用_ConnectionPtr,_RecordsetPtr来操纵数据库的。还有一个_CommandPtr,本程序没有使用它。

为了使用ADO,需要导入ADO动态链接库。在工程的stdafx.h文件中,添加如下代码:

?

1

2

3

4
//导入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类型的变量。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21
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时,必须先创建这种对象的一个实例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32
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中,这一次再放的时候,要重新创建一次,否则数据格式要么不匹配,要么保留有上一次的数据,定位困难。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34
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中添加代码

?

1

2
#include <GdiPlus.h>

using namespace Gdiplus;

在CApp类添加两个变量:

?

1

2
GdiplusStartupInput m_gdiplusstartUpInput;

ULONG_PTR m_GdiplusToken;

在CApp的InitInstance函数中添加

?

1
GdiplusStartup(&m_GdiplusToken, &m_gdiplusstartUpInput, NULL);

在ExitInstance函数中添加

?

1
GdiplusShutdown(m_GdiplusToken);

以下是读取图片数据并保存到本地的代码实现:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60
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是这个类的成员变量,所以

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69
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

}

至此就实现了存储图片和从数据库中把图片下载到本地的功能。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 C++使用ADO实现存取图片的方法 https://www.kuaiidc.com/76151.html

相关文章

发表评论
暂无评论