• C++使用ADO存取图片


    

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

    一、使用数据库前的准备

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

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

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

    //导入ADO
    #import "C:Program FilesCommon FilesSystemadomsado15.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
    }

    这样就实现了存储图片和从数据库中把图片下载到本地。

  • 相关阅读:
    Eclipse之JSP页面的使用
    Eclipse拷贝动态的web工程修改context root的值
    Redis的Cluster配置
    企业大数据战略定位
    批处理引擎MapReduce编程模型
    Python入门篇-基础数据类型之整型(int),字符串(str),字节(bytes),列表(list)和切片(slice)
    Listary 文件操作神器
    TextMesh Pro SpriteAsset Load From Assetbundle
    TextMesh Pro Emoji Align With Text(表情和文字对齐)
    使用Mkdocs构建你的项目文档
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7275609.html
Copyright © 2020-2023  润新知