使用 ADO 向数据库中存储一张图片

简介:   【备注】本文中所阐述代码应用于我为BS架构业务系统开发的某个 ActiveX 控件中。   我们将向一个典型SQL数据库中的某表的 Image 类型的字段(假设字段名称为“PHOTO”)存储一副图片,实际上 Image 字段是一种二进制流,它是由应用程序负责解释的。

  【备注】本文中所阐述代码应用于我为BS架构业务系统开发的某个 ActiveX 控件中。

 

  我们将向一个典型SQL数据库中的某表的 Image 类型的字段(假设字段名称为“PHOTO”)存储一副图片,实际上 Image 字段是一种二进制流,它是由应用程序负责解释的。因此在这里我们是将其当作 jpg 图像文件。换句话说,把 jpg 文件的原始字节流存储到 Image 字段中去。由于通过内存中转,这种文件的尺寸不宜过大。

  我们假设在一个CImage对象中已经加载的就是要保存的图片,同时也打开了相应表的一个用于插入记录的记录集指针 (_RecordsetPtr )。

  则相关代码如下:

  

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
// 存储图片
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”)。

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码_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);
    }
}

 


 

 

目录
相关文章
|
4月前
|
存储 关系型数据库 MySQL
MySQL——数据库备份上传到阿里云OSS存储
MySQL——数据库备份上传到阿里云OSS存储
178 0
|
2月前
|
存储 关系型数据库 MySQL
PACS系统 中 dicom 文件在mysql 8.0 数据库中的 存储和读取(pydicom 库使用)
PACS系统 中 dicom 文件在mysql 8.0 数据库中的 存储和读取(pydicom 库使用)
36 2
|
3月前
|
存储 SQL 专有云
支持配置审计日志的存储数据库
审计日志作为企业监管平台的重要依据,同时也是“等保三级”认证的必要考察项之一。Dataphin V4.3版本支持设置平台日志的存储数据源,帮助用户快速获取审计日志,同时介绍了不同部署模式的Dataphin如何查看审计日志的方法。
117 5
|
3月前
|
存储 NoSQL 关系型数据库
可以存储文件的数据库有哪些?
可以存储文件的数据库有哪些?
129 6
|
3月前
|
存储 NoSQL 关系型数据库
可以存储文件的数据库有哪些?
可以存储文件的数据库有哪些?
516 0
|
4月前
|
存储 缓存 负载均衡
带你认识DM 共享存储数据库集群
带你认识DM 共享存储数据库集群
87 3
|
4月前
|
存储 数据库
如何在数据库中存储小数:FLOAT、DECIMAL还是BIGINT?
【8月更文挑战第7天】在数据库中存储小数时,需谨慎选择数据类型:FLOAT、DECIMAL 或 BIGINT。FLOAT 存储空间小,适于非关键性小数如温度;但精度有限,可能产生误差。DECIMAL 能精确表示小数,适合货币金额等需要高度准确性的场景,不过占用空间较大。BIGINT 用于整数,若存储小数需额外转换处理。根据精度需求及应用场景选择合适类型至关重要。
104 2
|
4月前
|
存储 SQL 运维
“震撼发布!PolarDB-X:云原生分布式数据库巨擘,超高并发、海量存储、复杂查询,一网打尽!错过等哭!”
【8月更文挑战第7天】PolarDB-X 是面向超高并发、海量存储和复杂查询场景设计的云原生分布式数据库系统
110 1
|
4月前
|
存储 C# 关系型数据库
“云端融合:WPF应用无缝对接Azure与AWS——从Blob存储到RDS数据库,全面解析跨平台云服务集成的最佳实践”
【8月更文挑战第31天】本文探讨了如何将Windows Presentation Foundation(WPF)应用与Microsoft Azure和Amazon Web Services(AWS)两大主流云平台无缝集成。通过具体示例代码展示了如何利用Azure Blob Storage存储非结构化数据、Azure Cosmos DB进行分布式数据库操作;同时介绍了如何借助Amazon S3实现大规模数据存储及通过Amazon RDS简化数据库管理。这不仅提升了WPF应用的可扩展性和可用性,还降低了基础设施成本。
89 0
|
4月前
|
存储 小程序 关系型数据库
原生小程序 获取手机号并进行存储到mysql数据库
原生小程序 获取手机号并进行存储到mysql数据库