zoukankan      html  css  js  c++  java
  • 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
    }

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

  • 相关阅读:
    Mysql登录错误:ERROR 1045 (28000): Plugin caching_sha2_password could not be loaded
    Docker配置LNMP环境
    Docker安装mysqli扩展和gd扩展
    Docker常用命令
    Ubuntu常用命令
    单例模式的优缺点和使用场景
    ABP 多租户数据共享
    ABP Core 后台Angular+Ng-Zorro 图片上传
    ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
    AbpCore 执行迁移文件生成数据库报错 Could not find root folder of the web project!
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7275609.html
Copyright © 2011-2022 走看看