by xjsuez
2012年4月9日
最近做项目,使用oracle的BLOB保存大图片,将整理并测试成功的方法贴上来共享。
本人用平台是VS2008的C++模块,如果是VC的其他平台将代码做适当修改即可。代码用ADO连接ORACLE数据库 ,数据库版本为ORACLE 10G。
开始前,先建表,使用SQL*Plus建表:create table T_WS_PICTUREFILE(ID number(10),NAME varchar2(20),PICTURE blob;
设置ID为主键,并使用以下红色的SQL代码进行主键自增设置
SQL>create sequence emp_sequence
2 INCREMENT BY 1 -- 每次加几个
3 START WITH 1--从1开始计数
4 NOMAXVALUE-- 不设置最大值
5 NOCYCLE-- 一直累加,不循环
6 NOCACHE; -- 不建缓冲区
序列已创建。
SQL> create or replace trigger dectuser_tb_tri
2 before insert on TEST
3 for each row
4 begin
5 select emp_sequence.nextval into :new.ID from dual;
6 end;
7 /
触发器已创建
SQL> commit;
提交完成。
此时主键自增设置完毕,可以编代码了。
在stdafx.h文件中加入
#import "c:\program files\common files\system\ado\msado15.dll"no_namespacerename("EOF","adoEOF")
在类中添加变量
_ConnectionPtr m_pConn;
_RecordsetPtr m_pRecord;
char* m_pBMPBuffer;
DWORD m_nFileLen;
DWORD ChunkSize;
在初始化函数中初始化数据库连接
CoInitialize(NULL); //初始化Com组件
m_pConn.CreateInstance(__uuidof(Connection)); //Connection用于与数据库服务器的链接另一种方式
/******************连接数据库********************/
try
{
m_pConn->ConnectionTimeout = 5; //设置连接时间
m_pConn->Open("Provider=OraOLEDB.Oracle.1;Data Source=服务名;","数据库用户名","数据库密码",adModeUnknown);//连接oracle数据库,注意此处不能用Provider=MSDAORA.1
}
catch(_com_errore) //捕捉异常
{
AfxMessageBox(e.ErrorMessage());
}
CoUninitialize(); //释放com组件
在上传图片的函数中写入以下代码
CString dwlSpaceTime;//用于计算程序运行时间
DWORD dwBeginTime = ::GetTickCount(); /计算耗费时间///
int maxid;
maxid=GetMaxIDForBlob();
CString strSQL;
strSQL.Format(_T("Select * from T_WS_PICTURE where ID = %d"), maxid);
//strSQL.Format(_T("Select ID,NAME,utl_raw.cast_to_varchar2(PICTUREFILE) from T_WS_PICTURE where ID = %d"), maxid);
//下面向数据库中插入图片等。
//首先从数据库中读id最大的那条数据,主要目的是为了将RecordSet初始化
m_pRecord.CreateInstance(__uuidof(Recordset));
// 上面这句一定要注意,因为上一次把一些数据放入m_pRecord中,这一次再放的时候,要重新创建一次,否则数据格式要么不匹配,要么保留有上一次的数据,定位困难。
try
{
m_pConn-> CursorLocation = adUseClient; //可能会导查询变慢
m_pRecord->Open(_variant_t(strSQL),m_pConn.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);
}
catch (_com_error e)
{
AfxMessageBox(e.Description());
}
//
CString imagename;
char * pstrPathname;
//CString imagepath = _T("D:/test.bmp");
CString imagepath = _T("D:/Best.mim");
imagename = imagepath.Right(8);
CFile file;
if( !file.Open( imagepath, CFile::modeRead) )
{
AfxMessageBox(_T("打开文件失败!"));
return ;
}
m_nFileLen = file.GetLength();
m_pBMPBuffer = new char[m_nFileLen + 1];
if(!m_pBMPBuffer)
{
AfxMessageBox(_T("没有文件二进制流!"));
return ;
}
if(file.Read(m_pBMPBuffer,m_nFileLen) != m_nFileLen)
{
AfxMessageBox(_T("读取二进制文件流失败!"));
return ;
}
//
try
{
m_pRecord->AddNew(); //为记录集添加新的一行,更新时就会把这条新纪录放到数据库中
}
catch (_com_error e)
{
AfxMessageBox(_T("不能插入一条新的记录!"));
return;
}
try
{
int NewID=maxid+1;
m_pRecord->PutCollect("ID", (_bstr_t)NewID);//使用putcollect插入非图像数据,使用SetImage2DB插入图像数据
m_pRecord->PutCollect("NAME", (_bstr_t)imagename);
//blob二进制流
char *pBuf = m_pBMPBuffer;
VARIANT varBLOB;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];
if(pBuf)
{
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = m_nFileLen;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound); ///创建SAFEARRAY对象
for (long i = 0; i < (long)m_nFileLen; i++)
SafeArrayPutElement (psa, &i, pBuf++); ///将pBuf指向的二进制数据保存到SAFEARRAY对象psa中
varBLOB.vt = VT_ARRAY | VT_UI1; ///将varBLOB的类型设置为BYTE类型的数组
varBLOB.parray = psa; ///为varBLOB变量赋值
m_pRecord-> GetFields()-> GetItem("PICTUREFILE")-> AppendChunk(varBLOB);///加入BLOB类型的数据
}
//
}
catch (_com_error e)
{
AfxMessageBox(e.Description());
AfxMessageBox(_T("插入图片有异常"));
}
m_pRecord->Update();//使用完毕,关闭m_pRecord,并设置为NULL,最后关闭数据库连接
m_pRecord->Close();
m_pRecord = NULL;
delete m_pBMPBuffer;
DWORD dwEndTime = ::GetTickCount();/计算耗费时间///
DWORD dwSpaceTime = dwEndTime - dwBeginTime;
dwlSpaceTime.Format( _T("上传文件完成,耗时:%u 秒"),(dwSpaceTime/1000));//显示的是秒
AfxMessageBox(dwlSpaceTime);
//
需要添加另外一个函数:GetMaxIDForBlob
int C你的类::GetMaxIDForBlob(void)
{
CString strSQL;
int MaxID;
m_pRecord.CreateInstance(__uuidof(Recordset));
strSQL.Format(_T("Select count(*) as num, Max(ID) as maxid from T_WS_PICTURE"));
try
{
m_pRecord->Open(_variant_t(strSQL),m_pConn.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);
}
catch (_com_error e)
{
AfxMessageBox(_T("读取最大的id异常"));
return 0;
}
//从RecordSet中获取数据数目和当前数据库中最大的ID。
int num = m_pRecord->GetCollect("num");
if (num != 0)
{
MaxID = m_pRecord->GetCollect("maxid");
}
else
{
MaxID = 0;
}
m_pRecord->Close();//关闭记录集
m_pRecord.Release();//释放空间
return MaxID;
}
至此,上传BLOB文件代码完成
开始下载上传的文件
void C你的类::OnBnClickedDownloadFile()
{
// TODO: 在此添加控件通知处理程序代码
CString dwlSpaceTime;//用于计算程序运行时间
CString strSQL;
strSQL.Format(_T("Select * from T_WS_PICTURE where ID = 1")); //下面向数据库中插入图片等。修改ID可以下载不同的文件
DWORD dwBeginTime = ::GetTickCount(); /计算耗费时间///
//首先从数据库中读id最大的那条数据,主要目的是为了将RecordSet初始化
m_pRecord.CreateInstance(__uuidof(Recordset)); // 上面这句一定要注意,因为上一次把一些数据放入m_pRecord中,这一次再放的时候,要重新创建一次,否则数据格式要么不匹配,要么保留有上一次的数据,定位困难。
try
{
m_pConn-> CursorLocation = adUseClient; //可能会导查询变慢
m_pRecord->Open(_variant_t(strSQL),m_pConn.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);
}
catch (_com_error e)
{
AfxMessageBox(e.Description());
}
long lDataLength = m_pRecord-> GetFields()-> GetItem( "PICTUREFILE")-> ActualSize;///得到数据的长度
if (lDataLength>0)
{
_variant_t varBLOB;
varBLOB=m_pRecord->GetFields()->GetItem(_variant_t("PICTUREFILE"))->GetChunk(lDataLength);
if(varBLOB.vt== (VT_ARRAY|VT_UI1) && varBLOB.vt!=VT_EMPTY && varBLOB.vt!=VT_NULL )
{
BYTE *pBuf = NULL;
pBuf = (BYTE*)GlobalAlloc(GMEM_FIXED,lDataLength);
SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);
CString strFileName = _T("E:/test.bmp");
CFile outFile(strFileName,CFile::modeCreate|CFile::modeWrite); //构造新文件,如果文件存在,则长度变为0
outFile.Write(pBuf,lDataLength);
outFile.Close();
SafeArrayUnaccessData (varBLOB.parray);
}
}
m_pRecord->Close();
m_pRecord = NULL;
DWORD dwEndTime = ::GetTickCount();/计算耗费时间///
DWORD dwSpaceTime = dwEndTime - dwBeginTime;
dwlSpaceTime.Format( _T("下载文件完成,耗时:%u 秒"),(dwSpaceTime/1000));//显示的是秒
AfxMessageBox(dwlSpaceTime);
}
至此,编程完成,代码经过我测试没问题。如果有小问题根据实际情况修改即可。
代码刚写好,没怎么整理,可能有点乱,高手的板砖请轻拍。