数据库BLOB的存取方法(一)——AppendChunck/GetChunck

来源:互联网 发布:软件系统用户手册模板 编辑:程序博客网 时间:2024/05/08 00:28
 
         目的是把一个文件以二进制的形式读到内存中,然后存到数据库中。本以为和平常的方法一样,作为存储过程的参数,使用Command对象进行调用。但是遇到了很多问题:
1.         二进制数据中是以字节存储的,很多特殊的字符都可能出现。比如:字符串结束符,这样CStringFormat还有字符串的拷贝函数都不能用了(例如strcpy, sprintf等)。
2.         可以使用memcpy进行内存拷贝,但是由于特殊字符的存在,在知识Sql语句时还是会出错。虽然可以对特殊字符进行处理,例如将一个单引号替换成两个单引号,但是极可能出错。一个汉字是由两个字节来存储的,可能低位字节正好是一个单引号,如果替换了就会出错。
人都是被逼出来了,上网调研时,发现了BLOB的存储方法,BLOGBinary Large Object二进制大数据对象)。下面讲述其中的一种方法:AppendChunckGetChunck
 
一、创建表
以网上常用的userinfo表为例,表有四个字段:id——用户idusername——用户名称;old——用户年龄;photo——用户照片(二进制数据)。
 
可以用Postgre数据库,access数据库,Sql Server2000.
 
二、向数据库中写二进制数据
void CFileOperDlg::OnBnClickedButtonInsertChunck()
{
     // TODO: 在此添加控件通知处理程序代码
     _RecordsetPtr pRecordset( __uuidof(Recordset) );
     CString strSql = _T( "select * from /"userinfo/"" );
     try
     {
         _bstr_t varSql( strSql );
         // 打开记录集,这里使用普通的Select语句即可。
         HRESULT hr = pRecordset->Open( varSql, m_pConnection.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText );
         if ( FAILED( hr ) )
         {
              AfxMessageBox( _T( "打开记录集失败!" ) );
              return ;
         }
     }
     catch (_com_error & e)
     {
         AfxMessageBox(e.Description());
         return ;
     }
        
     VARIANT varBLOB;
     SAFEARRAY *psa;
     SAFEARRAYBOUND rgsabound[1];
     pRecordset->AddNew(); ///添加新记录
     pRecordset->PutCollect("id",_variant_t(12));                 // 给字段赋值
     pRecordset->PutCollect("username", _variant_t( "Owen" ) );
     pRecordset->PutCollect("old", _variant_t( 24 ) );
     if(m_pFileBuf)         // 保存文件数据的指针
     {
         rgsabound[0].lLbound = 0;
         rgsabound[0].cElements = m_dwFileLen;          // 文件数据的长度
         psa = SafeArrayCreate(VT_UI1, 1, rgsabound); ///创建SAFEARRAY对象
         for (long i = 0; i < (long)m_dwFileLen; i++)
              SafeArrayPutElement (psa, &i, m_pFileBuf++); ///将pBuf指向的二进制数据保存到SAFEARRAY对象psa中
         varBLOB.vt = VT_ARRAY | VT_UI1; ///将varBLOB的类型设置为BYTE类型的数组
         varBLOB.parray = psa; ///为varBLOB变量赋值
         pRecordset->GetFields()->GetItem("photo")->AppendChunk(varBLOB);///加入BLOB类型的数据
         SafeArrayDestroyData( psa );
     }
     pRecordset->Update(); ///保存我们的数据到库中
 
     pRecordset->Close();
     pRecordset.Release();
}
三、从数据库中读取数据
void CFileOperDlg::OnBnClickedButtonDownloadChunck()
{
     // TODO: 在此添加控件通知处理程序代码
     _RecordsetPtr pRecordset( __uuidof(Recordset) );
     CString strSql = _T( "select * from /"userinfo/" where id = 12" );
     try
     {
         _bstr_t varSql( strSql );
         // 打开记录集
         HRESULT hr = pRecordset->Open( varSql, m_pConnection.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText );
         if ( FAILED( hr ) )
         {
              AfxMessageBox( _T( "打开记录集失败!" ) );
              return ;
         }
     }
     catch (_com_error & e)
     {
         AfxMessageBox(e.Description());
         return ;
     }
 
     long lDataSize = pRecordset->GetFields()->GetItem("photo")->ActualSize;///得到数据的长度
     if(lDataSize > 0)
     {
         _variant_t varBLOB;
         varBLOB = pRecordset->GetFields()->GetItem("photo")->GetChunk(lDataSize);
         if(varBLOB.vt == (VT_ARRAY | VT_UI1)) ///判断数据类型是否正确
         {
              char *pBuf = NULL;
              SafeArrayAccessData(varBLOB.parray,(void **)&pBuf); ///得到指向数据的指针
              /*****在这里我们可以对pBuf中的数据进行处理*****/
              SafeArrayUnaccessData (varBLOB.parray);
              // 将pBuf的数据保存成文件
              writDataToFile( _T( "E://TestChunck.xls" ), (char*)pBuf, m_dwFileLen );
         }
     }
     // 如果RecordSet没有关机,将其关闭
     if ( pRecordset != NULL && pRecordset->GetState() == adStateOpen )
     {
         pRecordset->Close();
     }
     pRecordset.Release();
}
有一点值得注意:SafeArrayPutElement执行完毕,源Buffer就变成空了。我再释放Buffer时发现的。
原创粉丝点击