使用 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月前
|
存储 Oracle 关系型数据库
服务器数据恢复—光纤存储上oracle数据库数据恢复案例
一台光纤服务器存储上有16块FC硬盘,上层部署了Oracle数据库。服务器存储前面板2个硬盘指示灯显示异常,存储映射到linux操作系统上的卷挂载不上,业务中断。 通过storage manager查看存储状态,发现逻辑卷状态失败。再查看物理磁盘状态,发现其中一块盘报告“警告”,硬盘指示灯显示异常的2块盘报告“失败”。 将当前存储的完整日志状态备份下来,解析备份出来的存储日志并获得了关于逻辑卷结构的部分信息。
|
5月前
|
存储 关系型数据库 数据库
高性能云盘:一文解析RDS数据库存储架构升级
性能、成本、弹性,是客户实际使用数据库过程中关注的三个重要方面。RDS业界率先推出的高性能云盘(原通用云盘),是PaaS层和IaaS层的深度融合的技术最佳实践,通过使用不同的存储介质,为客户提供同时满足低成本、低延迟、高持久性的体验。
|
7月前
|
SQL 存储 分布式数据库
分布式存储数据恢复—hbase和hive数据库数据恢复案例
分布式存储数据恢复环境: 16台某品牌R730xd服务器节点,每台服务器节点上有数台虚拟机。 虚拟机上部署Hbase和Hive数据库。 分布式存储故障: 数据库底层文件被误删除,数据库不能使用。要求恢复hbase和hive数据库。
272 12
|
8月前
|
存储 SQL NoSQL
【赵渝强老师】达梦数据库的逻辑存储结构
本文介绍了达梦数据库的存储结构,包括逻辑和物理存储两部分。逻辑存储结构由数据库(Database)、表空间(Tablespaces)、段(Segments)、簇(Cluster)和页(Page)组成。数据库是最大逻辑单元,包含所有表、索引等;表空间由数据文件组成,用于存储对象;段由簇构成,簇包含连续的数据页;页是最小存储单元。文中还提供了查询表空间、段和页大小的SQL语句,并附有视频讲解和示意图。
326 7
|
9月前
|
存储 关系型数据库 分布式数据库
PolarDB开源数据库进阶课3 共享存储在线扩容
本文继续探讨穷鬼玩PolarDB RAC一写多读集群系列,介绍如何在线扩容共享存储。实验环境依赖《在Docker容器中用loop设备模拟共享存储》搭建。主要步骤包括:1) 扩容虚拟磁盘;2) 刷新loop设备容量;3) 使用PFS工具进行文件系统扩容;4) 更新数据库实例以识别新空间。通过这些步骤,成功将共享存储从20GB扩容至30GB,并确保所有节点都能使用新的存储空间。
193 1
|
8月前
|
存储 SQL 安全
【赵渝强老师】达梦数据库的物理存储结构
本文介绍了达梦数据库的存储结构及各类物理文件的作用。达梦数据库通过逻辑和物理存储结构管理数据,包含配置文件(如dm.ini、sqllog.ini)、控制文件(dm.ctl)、数据文件(*.dbf)、重做日志文件(*.log)、归档日志文件、备份文件(*.bak)等。配置文件用于功能设置,控制文件记录数据库初始信息,数据文件存储实际数据,重做日志用于故障恢复,归档日志增强数据安全性,备份文件保障数据完整性,跟踪与事件日志辅助问题分析。这些文件共同确保数据库高效、稳定运行。
410 0
|
9月前
|
存储 人工智能 监控
时序数据库 TDengine 化工新签约:存储降本一半,查询提速十倍
化工行业在数字化转型过程中面临数据接入复杂、实时性要求高、系统集成难度大等诸多挑战。福州力川数码科技有限公司科技依托深厚的行业积累,精准聚焦行业痛点,并携手 TDengine 提供高效解决方案。
188 0
|
11月前
|
存储 druid 分布式数据库
列式存储数据库与超市的关系?
列式存储数据库是一种高效的数据管理方式,类似于超市将相似商品集中摆放。它将相同类型的数据(如年龄、价格)归类存储,便于快速查询和压缩,广泛应用于市场分析、财务报告和健康数据分析等领域。知名产品包括HBase、ClickHouse、Druid和Apache Cassandra等,适合处理大规模数据和实时分析任务。
158 4
|
存储 数据库
快速搭建南大通用GBase 8s数据库SSC共享存储集群
本文介绍如何GBase8s 数据库 在单机环境中快速部署SSC共享存储集群,涵盖准备工作、安装数据库、创建环境变量文件、准备数据存储目录、修改sqlhost、设置onconfig、搭建sds集群及集群检查等步骤,助你轻松完成集群功能验证。
|
11月前
|
存储 Oracle 关系型数据库
服务器数据恢复—华为S5300存储Oracle数据库恢复案例
服务器存储数据恢复环境: 华为S5300存储中有12块FC硬盘,其中11块硬盘作为数据盘组建了一组RAID5阵列,剩下的1块硬盘作为热备盘使用。基于RAID的LUN分配给linux操作系统使用,存放的数据主要是Oracle数据库。 服务器存储故障: RAID5阵列中1块硬盘出现故障离线,热备盘自动激活开始同步数据,在同步数据的过程中又一块硬盘离线,RAID5阵列瘫痪,上层LUN无法使用。

热门文章

最新文章