目的是把一个文件以二进制的形式读到内存中,然后存到数据库中。本以为和平常的方法一样,作为存储过程的参数,使用
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时发现的。