【备注】本文中所阐述代码应用于我为BS架构业务系统开发的某个 ActiveX 控件中。
我们将向一个典型SQL数据库中的某表的 Image 类型的字段(假设字段名称为“PHOTO”)存储一副图片,实际上 Image 字段是一种二进制流,它是由应用程序负责解释的。因此在这里我们是将其当作 jpg 图像文件。换句话说,把 jpg 文件的原始字节流存储到 Image 字段中去。由于通过内存中转,这种文件的尺寸不宜过大。
我们假设在一个CImage对象中已经加载的就是要保存的图片,同时也打开了相应表的一个用于插入记录的记录集指针 (_RecordsetPtr )。
则相关代码如下:
代码
//
存储图片
bool SaveImage(CImage * lpImg, _RecordsetPtr pRecordset, char * errormsg)
{
// 估算图像需要的内存大小,这里当作 BMP 格式来估算的,所以结果比实际需要的更大。
SIZE_T buffersize = lpImg -> GetWidth() * lpImg -> GetHeight() * lpImg -> GetBPP() / 8 + sizeof (BITMAPINFO);
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, buffersize);
if (hMem != NULL)
{
IStream * pStream = NULL;
CreateStreamOnHGlobal(hMem, FALSE, & pStream);
if (pStream != NULL)
{
LARGE_INTEGER temp;
ULARGE_INTEGER fileLength;
temp.QuadPart = 0 ;
lpImg -> Save(pStream); // 写入内存流
// 获取当前文件的位置
pStream -> Seek(temp, STREAM_SEEK_CUR, & fileLength);
ULONG nLength = (ULONG)(fileLength.QuadPart + 1 );
SAFEARRAY * psa;
SAFEARRAYBOUND rgsabound[ 1 ];
rgsabound[ 0 ].lLbound = 0 ;
rgsabound[ 0 ].cElements = nLength;
// VT_UI1 : Variable type is unsigned char.
psa = SafeArrayCreate(VT_UI1, 1 , rgsabound);
// 锁定内存
BYTE * lpBytes = (BYTE * )GlobalLock(hMem);
BYTE * lpTemp = lpBytes;
for (LONG i = 0 ; i < nLength; i ++ )
SafeArrayPutElement (psa, & i, lpTemp ++ );
// 解锁内存
GlobalUnlock(hMem);
VARIANT varBLOB;
varBLOB.vt = VT_ARRAY | VT_UI1;
varBLOB.parray = psa;
// 存储
pRecordset -> Fields -> GetItem( " PHOTO " ) -> AppendChunk(varBLOB);
pStream -> Release();
//
SafeArrayDestroyData(psa);
}
else
{
GlobalFree(hMem);
sprintf(errormsg, " CreateStreamOnHGlobal Failed " );
return false ;
}
// 释放全局内存
GlobalFree(hMem);
}
else
{
sprintf(errormsg, " GlobalAlloc Failed! " );
return false ;
}
return true ;
}
bool SaveImage(CImage * lpImg, _RecordsetPtr pRecordset, char * errormsg)
{
// 估算图像需要的内存大小,这里当作 BMP 格式来估算的,所以结果比实际需要的更大。
SIZE_T buffersize = lpImg -> GetWidth() * lpImg -> GetHeight() * lpImg -> GetBPP() / 8 + sizeof (BITMAPINFO);
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, buffersize);
if (hMem != NULL)
{
IStream * pStream = NULL;
CreateStreamOnHGlobal(hMem, FALSE, & pStream);
if (pStream != NULL)
{
LARGE_INTEGER temp;
ULARGE_INTEGER fileLength;
temp.QuadPart = 0 ;
lpImg -> Save(pStream); // 写入内存流
// 获取当前文件的位置
pStream -> Seek(temp, STREAM_SEEK_CUR, & fileLength);
ULONG nLength = (ULONG)(fileLength.QuadPart + 1 );
SAFEARRAY * psa;
SAFEARRAYBOUND rgsabound[ 1 ];
rgsabound[ 0 ].lLbound = 0 ;
rgsabound[ 0 ].cElements = nLength;
// VT_UI1 : Variable type is unsigned char.
psa = SafeArrayCreate(VT_UI1, 1 , rgsabound);
// 锁定内存
BYTE * lpBytes = (BYTE * )GlobalLock(hMem);
BYTE * lpTemp = lpBytes;
for (LONG i = 0 ; i < nLength; i ++ )
SafeArrayPutElement (psa, & i, lpTemp ++ );
// 解锁内存
GlobalUnlock(hMem);
VARIANT varBLOB;
varBLOB.vt = VT_ARRAY | VT_UI1;
varBLOB.parray = psa;
// 存储
pRecordset -> Fields -> GetItem( " PHOTO " ) -> AppendChunk(varBLOB);
pStream -> Release();
//
SafeArrayDestroyData(psa);
}
else
{
GlobalFree(hMem);
sprintf(errormsg, " CreateStreamOnHGlobal Failed " );
return false ;
}
// 释放全局内存
GlobalFree(hMem);
}
else
{
sprintf(errormsg, " GlobalAlloc Failed! " );
return false ;
}
return true ;
}
下面再列举一下,如何从 image 字段中读取内容,并把它保存到一个磁盘上的普通文件。假设表具有一个自增的数字主键(“ID”)。
代码_ReadImage
//
id:主键
void ReadImg( int id)
{
_RecordsetPtr pRs = NULL;
_ConnectionPtr pConnection = NULL;
_variant_t varChunk;
HRESULT hr;
// 连接字符串
_bstr_t strCnn( " Provider=SQLOLEDB;Server=...;Database=...;User ID=...;Password=...; " );
try
{
// Open a connection
pConnection.CreateInstance(__uuidof(Connection));
hr = pConnection -> Open(strCnn, "" , "" ,NULL);
pRs.CreateInstance(__uuidof(Recordset));
// 表名略
char cmdText[ 128 ];
sprintf(cmdText, " select PHOTO from ... where ID = %d " , id);
pRs -> Open(cmdText,_variant_t((IDispatch * ) pConnection, true ),adOpenKeyset,adLockOptimistic,adCmdText);
// read data
long lPhotoSize = pRs -> Fields -> GetItem( " PHOTO " ) -> ActualSize;
long llsRead = 0 ;
_variant_t varChunk;
BYTE buf[ChunkSize];
printf( " lDatalength = %ld\n " , lPhotoSize);
// 保存到C盘
char filename[ 128 ];
sprintf(filename, " C:\\ID_%d.jpg " , id);
FILE * stream = fopen(filename, " wb " );
while (lPhotoSize > 0 )
{
llsRead = lPhotoSize >= ChunkSize ? ChunkSize:lPhotoSize;
varChunk = pRs -> Fields -> GetItem( " PHOTO " ) -> GetChunk(llsRead);
for ( long index = 0 ; index < llsRead; index ++ )
{
SafeArrayGetElement(varChunk.parray, & index, buf + index);
}
fwrite(buf, 1 , llsRead, stream);
lPhotoSize -= llsRead;
}
fclose(stream);
printf( " Save File Complete: %s\n " , filename);
pRs -> Close();
pConnection -> Close();
}
catch (_com_error & e)
{
// Notify the user of errors if any.
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
CString sError;
sError.Format(_T( " Source : %s \n Description : %s\n " ),(LPCSTR)bstrSource,(LPCSTR)bstrDescription);
// AfxMessageBox(sError);
// printf("%s\n", sError);
}
}
void ReadImg( int id)
{
_RecordsetPtr pRs = NULL;
_ConnectionPtr pConnection = NULL;
_variant_t varChunk;
HRESULT hr;
// 连接字符串
_bstr_t strCnn( " Provider=SQLOLEDB;Server=...;Database=...;User ID=...;Password=...; " );
try
{
// Open a connection
pConnection.CreateInstance(__uuidof(Connection));
hr = pConnection -> Open(strCnn, "" , "" ,NULL);
pRs.CreateInstance(__uuidof(Recordset));
// 表名略
char cmdText[ 128 ];
sprintf(cmdText, " select PHOTO from ... where ID = %d " , id);
pRs -> Open(cmdText,_variant_t((IDispatch * ) pConnection, true ),adOpenKeyset,adLockOptimistic,adCmdText);
// read data
long lPhotoSize = pRs -> Fields -> GetItem( " PHOTO " ) -> ActualSize;
long llsRead = 0 ;
_variant_t varChunk;
BYTE buf[ChunkSize];
printf( " lDatalength = %ld\n " , lPhotoSize);
// 保存到C盘
char filename[ 128 ];
sprintf(filename, " C:\\ID_%d.jpg " , id);
FILE * stream = fopen(filename, " wb " );
while (lPhotoSize > 0 )
{
llsRead = lPhotoSize >= ChunkSize ? ChunkSize:lPhotoSize;
varChunk = pRs -> Fields -> GetItem( " PHOTO " ) -> GetChunk(llsRead);
for ( long index = 0 ; index < llsRead; index ++ )
{
SafeArrayGetElement(varChunk.parray, & index, buf + index);
}
fwrite(buf, 1 , llsRead, stream);
lPhotoSize -= llsRead;
}
fclose(stream);
printf( " Save File Complete: %s\n " , filename);
pRs -> Close();
pConnection -> Close();
}
catch (_com_error & e)
{
// Notify the user of errors if any.
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
CString sError;
sError.Format(_T( " Source : %s \n Description : %s\n " ),(LPCSTR)bstrSource,(LPCSTR)bstrDescription);
// AfxMessageBox(sError);
// printf("%s\n", sError);
}
}