凡扬华
(同济大学 软件学院,上海 201900)
IPicture控件实现图片数据在数据库中的存取
凡扬华
(同济大学 软件学院,上海 201900)
针对数据库应用程序开发中以往图片类型数据存取方法存在一致性、安全性及完整性较差的缺点,利用数据库管理系统提供的一种名为二进制大对象(BLOB)的字段类型,提出了一种利用IPicture控件实现图片数据在数据库大对象字段中的存取方法。
数据库访问技术;IPicture控件;图片数据
为了便于建模,一般的数据库管理系统都提供了字符型字段、数值型字段等常见数据类型,有的数据库管理系统,比如DB2,也提供了用于保存复杂结构的数据类型,如图片、文档等,将对象保存为长二进制数据类型。但对于二进制数据类型,不能采用像一般数据类型那样的赋值操作,因而存取起来比较复杂。一种普遍采用的变通方法是将本地图像(文档)文件的路径作为字符串保存在数据库字段中,然后根据字符串,索引数据库外的图像(文档)文件完成访问功能。但该方法有一个非常明显的缺点,即大对象文件并不是保存在数据库字段中,数据库管理系统提供的有关数据的安全性、完整性及一致性等良好特性未能得到有效实施。本文利用IPicture控件,从分析其核心功能入手,结合内存数据交换技术,介绍了一种将图像文件直接存入数据库字段的技术。
IPicture控件采用微软COM接口技术,封装了常见格式(如*.jpg,*.bmp,*.png等)的图片操作,下面对相关的主要函数接口进行介绍。
1.1 从文件中载入图片
从文件中载入图片使用的OleLoadPicture函数如下:
OleLoadPicture(pStm,dwSize,FALSE,IID_IPicture,(LPVOID*)&m_pPic)
其中:pStm为IStream*类型的COM接口指针,需事先在内存中创建,并装载了来自图片文件的数据流;dwSize为指明载入图片数据的字节数,即图片文件的大小;FALSE为布尔值,这里传入FALSE很关键,否则对流的操作将失效;IID_IPicture为接口标识符;m_pPic为IPicture*类型的COM接口指针,即载入图片的存放处。
其关键技术是在内存中建立图片数据流,其实现的关键代码如下:
//省略错误处理部分
CFile file;
file.Open(strFileName,CFile::modeRead);
DWORD dwSize=file.GetLength();
//在内存中分配堆空间
HGLOBAL hMem=GlobalAlloc(GMEM_MOVEABLE, dwSize);
LPVOID pDes = ::GlobalLock(hMem);
//通过文件读操作将图片内容载入堆空间:
file.Read(pDes,dwSize);
//在堆空间上建立图片数据流pStm:
IStream *pStm=NULL;
CreateStreamOnHGlobal(hMem,TRUE,&pStm);
通过以上代码,IPicture*类型的控件变量m_pPic即在内存中保存了图片数据流,并且该数据流可以用于后续的存入数据库操作。
1.2 显示图片
装载了图片数据的IPicture控件,主要功能之一是用于图片显示,其用到成员函数Render()。该功能比较容易实现,本文不详述。
在Visual C++ 6.0开发环境下,数据库的访问比较常用的方法是采用微软的MFC ODBC技术。MFC ODBC封装了CDatabase和CRecordset类,用于建立数据库连接和从数据库中获取查询数据集或更新数据库。其中,可以在CRecordset类型的对象中传入SQL查询语句,并通过列绑定和列交换就可以将数据库字段值交换为CRecordset对象成员变量值,这里不详述。
虽然数据库中的BLOB类型字段可以通过列交换技术保存在CRecordset对象的成员变量中,但CLongBinary类型的成员变量并不能用于直接存取。因此,利用IPicture控件和内存数据交换技术可以实现对此二进制数据流的存取技术。首先将CLongBinary类型的二进制流数据拷贝给内存堆空间,然后在内存堆空间上建立IStream*类型的数据流,最后用OleLoadPicture(...)函数将数据流载入IPicture控件。其实现的关键代码如下:
//将二进制数据拷贝给内存堆空间:
//lob是CLongBinary类型的变量,对应图像字段
//一般通过CRecordset类的列交换技术获得。
LPSTR pBuff=(LPSTR)GlobalLock(lob.m_hData);
HGLOBAL hGlobal=GlobalAlloc(GMEM_MOVEABLE,
Lob.m_dwDataLength);
void* pData=GlobalLock(hGlobal);
memcpy(pData,pBuff,lob.m_dwDataLength);
GlobalUnlock(hGlobal);
//在内存空间上建立IStream流
IStream *pStream=NULL;
CreateStreamOnHGlobal(hGlobal,TRUE,&pStream);
//读入数据流到IPicture控件
OleLoadPicture(pStream,nSize,FALSE,IID_IPicture,(LPVOID*)&m_pPic)
通过以上代码,实现了将数据库中图片数据读取到IPicture控件中的功能。该IPicture控件对象可用于显示图片内容或将图片内容存入文件或数据库。
将IPicture控件中的图片数据存入数据库字段中是第2节中所述的反向操作。这里,关键是如何获得IPicture组件中的数据流,本文通过对IPicture接口的研究,开发了实现技术,其主要代码如下:
//申请内存空间,并创建IStream流,与内存绑定
IStream* pSrcStream=NULL;
//m_dwImgSize变量:从文件中读入图像时保存的图像大小
HGLOBAL hMemSrc=::GlobalAlloc(GPTR,m_dwImgSize);
BYTE *pMem=0;
if(hMemSrc!=NULL)
{
pMem=(BYTE*)GlobalLock(hMemSrc);
if(pMem!=NULL)
{
//在申请的内存空间上绑定IStream型变量
hr=::CreateStreamOnHGlobal(hMemSrc,TRUE,&pSrcStream);
GlobalUnlock(hMemSrc);
}
else//出错处理
{
GlobalFree(hMemSrc);
hr=E_OUTOFMEMORY;
}
}
else
{
hr=E_OUTOFMEMORY;
}
if(NULL==pSrcStream)
return hr;
//通过pSrcStream将IPicture图像数据存入内存区块pMem;
LONG cbSize=0;
//SaveAsFile函数起了关键作用。
hr=m_pPic->SaveAsFile(pSrcStream,FALSE,&cbSize);
if(FAILED(hr))
{
pSrcStream->Release();
pSrcStream=NULL;
return hr;
}
else
{
//将pSrcStream指针置于数据起始位置,以便后续写入字段操作。
hr=pSrcStream->Commit(STGC_DEFAULT);
LARGE_INTEGER dlibMove;
ULARGE_INTEGER dlibNew;
memset(&dlibMove,0,sizeof(dlibMove));
hr=pSrcStream->Seek(dlibMove,STREAM_SEEK_SET,&dlibNew);
}
//利用源流pSrcStream写入目标流(字段)
if(pMem)
{
//lob是CLongBinary类型的变量,对应图像字段
lob.m_dwDataLength=m_dwImgSize;
HGLOBAL hGlobal=GlobalAlloc(GPTR,m_dwImgSize);//申请存放图像数据的空间。
lob.m_hData = GlobalLock(hGlobal);//将该空间赋给m_hData成员;
//向图像字段写入IPicture数据
memcpy(lob.m_hData,pMem,m_dwImgSize);
GlobalUnlock(hGlobal);
pSrcStream->Release();
pSrcStream=NULL;
}
上述代码关键点为IPicture控件成员函数SaveAsFile的应用,并注意第二参数应设置为FALSE,它实现了提取控件中的图像数据流并存入内存区块的目的。其他代码都是围绕此图像数据区块的数据交换操作,以实现将图像数据存入数据库字段的功能。4 封装一个通用类CPictureField
利用 C++语言面向对象的良好特性,设计出针对数据库图像字段的通用类CPictureField。该类封装了上述介绍的主要功能,类型声明主要如下:
class CPictureField{
public:
//将IPicture图像数据存入数据库字段,
//通过CRecordset接口可以获取目标字段
//通常是二进制数据流类型CLongBinary
HRESULT DumpToField(CRecordset& set);
//将IPicture图像数据存入文件
HRESULT DumpToFile(LPCTSTR szFileName);
//将数据库图像数据载入IPicture控件
bool LoadFromField(CRecordset& set);
//将图像文件内容载入IPicture控件//同时,该函数保存了图像大小m_dwImgSize
bool LoadFromFile(LPCTSTR strFileName);
……
protected:
DWORD m_dwImgSize;
……
//核心部件:IPicture控件指针
IPicture* m_pPic;
};
CPictureField类实现的主要功能有:①从硬盘图像文件中载入图像数据;②将图像数据写入数据库字段;③将图像数据写入硬盘文件;④从数据库字段中载入图像数据。
本文介绍了在C++编程语言环境下(基于Visual C++ 6.0版本),利用IPicture控件,结合内存数据交换技术,进行数据库图像字段的直接存取,解决了常规方法的数据一致性、安全性及完整性方面的问题。通过项目的实际应用,验证了该技术思路的可行性。同时,本文给出了实现此类功能的核心代码,读者可以根据自己项目开发的实际需要,稍加修改及整合,即可应用到具体的项目实践中。
[1] 李闽溟,吴继刚.Visual C++ 6.0数据库系统开发实例导航[M].北京:人民邮电出版社,2002.
Access Image Data in A Database by Using IPicture Control
FAN Yang-hua
(School of Software Engineering, Tongji University, Shanghai 201900, China)
To the problem of poor consistency, security and integrity in the storage of image data in the development of database application program, an access method of the image data by using IPicture control in large database is proposed, which is based on binary large object (BLOB) field types provided by database management systems.
technology of database access; IPicture control; image data
1672- 6413(2015)06- 0036- 02
2015- 01- 04;
2015- 09- 27
凡扬华(1976-),男,湖南郴州人,工程师,本科, 现从事钢铁产品工艺设计管理工作 。
TP274
A