在项目中。我们须要把事故简图上传到总server。以便每一个client都能下载或者查看。在网上找了找,向Server2000存储图片代码比較多,从数据库中读取图片并显示也不少,可是把图片从数据库中二进制数据转换为原图片保存在本地,就非常少有C++代码了。花了大约4天时间,和师妹两个人找各种资料,最终攻克了这个问题。以下就一步一步地讲一讲我们的解决方法:

一、使用数据库前的准备

我们使用ADO,是用_ConnectionPtr,_RecordsetPtr来操纵数据库的。

另一个_CommandPtr,本程序没有使用它。

为了使用ADO。须要导入ADO动态链接库。在project的stdafx.h文件里。加入例如以下代码:

//导入ADO

#import "C:\Program Files\Common Files\System\ado\msado15.dll"\

    rename_namespace("ADOCG")rename("EOF","EndOfFile")

using namespace ADOCG;

这些代码声明,在这个project中使用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存取图片的更多相关文章

  1. iOS FMDB的使用(增,删,改,查,sqlite存取图片)

    iOS FMDB的使用(增,删,改,查,sqlite存取图片) 在上一篇博客我对sqlite的基本使用进行了详细介绍... 但是在实际开发中原生使用的频率是很少的... 这篇博客我将会较全面的介绍FM ...

  2. SharedPreferences详解(三)——存取图片

    MainActivity如下: package cc.sp; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputSt ...

  3. [转载]mongoDB学习笔记——存取图片(C#)

    作为一个NoSql数据库的代表,存取多媒体数据,应该是强项吧?那么,图片在mongoDB里是如何存取的呢?(其实,关系型数据库存取图片也一点问题没有,所以我看NoSql的强项不在于是否存储多媒体,而在 ...

  4. delphi向SQL Server2005中存取图片

    SQL Server2005中,我用image类型来存取图片,首先把数据库表设置好 例如我的pic表有如下两列:时间,图片. delphi中,我用ADOQuery来连接数据库,但是数据库中有好几张表, ...

  5. c#在sql中存取图片image示例

    这篇文章主要介绍了c#在sql中存取图片image示例,需要的朋友可以参考下 (1)控制台应用程序下演示插入图片 复制代码 代码如下: public void InsertIMG() { //将需要存 ...

  6. mongoDB学习笔记——存取图片(C#)

    作为一个NoSql数据库的代表,存取多媒体数据,应该是强项吧?那么,图片在mongoDB里是如何存取的呢?(其实,关系型数据库存取图片也一点问题没有,所以我看NoSql的强项不在于是否存储多媒体,而在 ...

  7. 【转】Windows Phone在隔离存储里存取图片文件

    一共两个页面,第一个为MainPage.xaml,代码如下: <!--ContentPanel - place additional content here--> <Grid x: ...

  8. ASP.Net数据库如何存取图片

    当我们有大量的图片或者图片比较大时,我们常规的做法可能是保存图片路径,但是也不排除需要将图片直接存放到数据库的情况,此时就需要保存图片到数据库了.这篇文章我会向大家介绍: 如何通过FileUpLoad ...

  9. delphi存取图片

    1.存图片到数据库 var   PicStream: TMemoryStream; if imgBugPic.Picture.Graphic <> nil then  begin    P ...

随机推荐

  1. ajax跨域的解决方案

    前言 公司要做一个活动页面,在其过程中发现所有的接口,ajax请求跨域.这里对跨域做个简单介绍以及提供几种解决办法. 由于浏览器实现的同源策略的限制,XmlHttpRequest只允许请求当前源(域名 ...

  2. 安装Ubuntu Linux分区的推荐方案和方法

    在安装Ubuntu Linux之前,你必须先取得安装软件,(网络上有许多mirror站台可供下载,下载ISO文件后自行刻录光盘或采用硬盘安装).当一切都准备就绪后,我们还得先了解该如何规划我们的硬盘. ...

  3. TJSONTableObject跨平台解析JSON

    TJSONTableObject跨平台解析JSON USES SynCrossPlatformJSON TSQLRecordPeoplePersistent = class(TPersistent) ...

  4. 解决新浪微博API调用限制 突破rate_limit_status瓶颈

    新浪微博开放平台API的调用和TWITTER接口一样,都是受限的,以小时为单位进行限定. 他有两个限制原则 1.用户不登录基于IP的限制,每小时1000次 2.用户登录了基于用户的限制,每小时1000 ...

  5. Lua学习笔记之开始

    在这节课的开始,按照国际惯例,一般学习一门新的语言,都是要打印出“Hello,world”,那么我们也不例外. Lua语言的打印跟其他语言一样,只要print(“Hello,World”),然后保存为 ...

  6. SQL盲注测试高级技巧

    写在前面: 这篇文章主要写了一些加快盲注速度的技巧和盲注中比较精巧的语句,虽然注入并不是什么新技术了.但是数据库注入漏洞依然困扰着每一个安全厂商,也鞭策着每一个安全从业者不断前进. 正文: 首先来简单 ...

  7. Git系列四之在本地服务器搭建gitlab仓库管理

    1.Git仓库管理 现在本地已经创建了git仓库,又在gitlab上创建了一个git仓库,并且让这两个仓库进行远程同步,这样gitlab仓库既可以备份也可以与他人协作管理远程仓库以及根据需要推送或拉取 ...

  8. form表单提交时选择性传值到后台

    正常情况下form表单提交会把表单内的内容提交到后台,但是如果有些内容只是作为展示或者是标记而不想传到后台,我们采用如下方法: jsp页面如下,我们不想提交id为userIdMark和pwdMark的 ...

  9. PhantomJS + Selenium webdriver 总结-元素定位

    webdriver提供了丰富的API,有多种定位策略:id,name,css选择器,xpath等,其中css选择器定位元素效率相比xpath要高些,使用id,name属性定位元素是最可靠,效率最高的一 ...

  10. IE下有没有类似于Firebug的调试工具

    你可以从以下网站下载firebuglite(Firebug推出的针对非火狐浏览器的调试工具) https://getfirebug.com/firebuglite   一般安装Stable chann ...