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

 
         目的是把一个文件以二进制的形式读到内存中,然后存到数据库中。本以为和平常的方法一样,作为存储过程的参数,使用 Command 对象进行调用。但是遇到了很多问题:
1.         二进制数据中是以字节存储的,很多特殊的字符都可能出现。比如:字符串结束符,这样 CString Format 还有字符串的拷贝函数都不能用了(例如 strcpy, sprintf 等)。
2.         可以使用 memcpy 进行内存拷贝,但是由于特殊字符的存在,在知识 Sql 语句时还是会出错。虽然可以对特殊字符进行处理,例如将一个单引号替换成两个单引号,但是极可能出错。一个汉字是由两个字节来存储的,可能低位字节正好是一个单引号,如果替换了就会出错。
人都是被逼出来了,上网调研时,发现了 BLOB 的存储方法, BLOG Binary Large Object 二进制大数据对象)。下面讲述其中的一种方法: AppendChunck GetChunck
 
一、创建表
以网上常用的 userinfo 表为例,表有四个字段: id ——用户 id username ——用户名称; 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时发现的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值