【备注】本文中所阐述代码应用于我为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;
}

  下面再列举一下,如何从 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);
    }
}

使用 ADO 向数据库中存储一张图片相关推荐

  1. python获取数据库用户名密码_在数据库中存储用户和密码

    我正在创建一个用户+密码的软件.认证后,用户可以访问一些半公共服务,但也可以加密一些只有用户才能访问的文件.在 用户必须按原样存储,如有可能,无需修改.在auth之后,只要软件还在运行,用户和密码都会 ...

  2. android studio数据库存储数据,如何使用API​​ 23在android studio中的数据库中存储数据?...

    大多数时候我不会发布任何内容,因为我可以在其他帖子中找到我需要的所有内容,但是现在我已经有几天了,您如何在数据库中存储任何内容?这是我的Java代码如何使用API​​ 23在android studi ...

  3. 数据库中存储日期的字段类型究竟应该用varchar还是datetime ?

    背景: 前段时间在百度经验看到一篇文章<如何在电脑右下角显示你(爱人)的名字>,之前也听过这个小技巧,但没真正动手设置过.所以出于好奇就实践了一下. 设置完成后的效果例如以下.右下角的时间 ...

  4. 数据库中存储用户名、密码时如何处理?

    一般的项目都有一个用户表,请问在这个表中,你的账号和密码都是明文存储的么?那么怎么防止被别人看见用户的密码呢? 我见过一个项目是这样的,在用户注册时就对用户的密码进行MD5加密,这样用户表中存储的密码 ...

  5. 数据库中存储Json格式数据

    在数据库中存储Json格式数据 1.表字段类型 json 2.Java代码有两种方式: 方式一 :属性定义成String类型. 往数据库中存储的值 必须为JSON格式的字符串,因为数据库中会做一次校验 ...

  6. 在SQL数据库中存储纬度和经度数据时要使用的数据类型是什么? [重复]

    本文翻译自:What datatype to use when storing latitude and longitude data in SQL databases? [duplicate] Th ...

  7. 给Double赋值为0.0时,数据库中存储为null

    此分值字段为Double类型,给他赋值0.0后,数据库中依旧为null xml中的sql语句为这样. 解决方法 将sql语句修改为 即可 数据库中存储为

  8. 树结构如何在关系型数据库中存储

    在数据库中存储分层数据 ​ 无论您是想建立自己的论坛,还是想在网站上发布邮件列表中的消息,还是想编写自己的cms:总有一天,您会希望将分层数据存储在数据库中.而且,除非您使用类似XML的数据库,否则表 ...

  9. 性别字段在数据库中存储数字,查询时,如何查询出数字对应的男和女?(case when的应用)

    今天敲代码的时候,刚好遇到这个问题,写博客记一下. 情况如下:性别字段在数据库中存储的是数字,男对应1,女对应0.然后,查询的时候,我想查询出男和女两个字,而不是1和0. 一开始,我写的sql语句,是 ...

最新文章

  1. 山社电机: SAMSR -外部接口测试
  2. C#中使用ProtoBuf将list进行序列化并保存到文件
  3. JavaScript高级程序设计阅读笔记
  4. shiro前后端分离_为什么要前后端分离?前后端分离的优点是什么?
  5. 更新10_linux,时隔十年,QQ更新了Linux版本
  6. 5G赋能中国智慧教育
  7. 用shell查看关键数据
  8. 通过js跳转url下载包含中文的文件乱码问题解决方案(java)
  9. 关于TikTok的变现思考和三种玩法
  10. Python 2.x vs Python 3(三)
  11. 不依赖第三方环境和服务
  12. 【数据结构】可以逃课其它字符串算法的字符串哈希算法
  13. 数据绑定的优点_轻松应对海量数据,TiDB 在车好多的实践
  14. node on mac
  15. inception-v1 自复现 有问题尽管问
  16. python逻辑回归模型建模步骤_逻辑回归建模及变量重要性可视化(Python实现)
  17. 目标检测(四):SSD之Pytorch源码解读
  18. 倍福Twincat 3.0软件的EAP通讯(补充)
  19. 最小二乘、加权最小二乘(WLS)、迭代加权最小二乘(迭代重加全最小二乘)(IRLS)
  20. STM32调试诊断工具 | STM Studio介绍、下载、安装和使用教程

热门文章

  1. mysqli得到记录数量
  2. C++程序设计实践题1
  3. 秒杀安全狗的经验总结
  4. C# 6.0 的那些事
  5. 【DFS + 记忆化递归 + DP】LeetCode 91. Decode Ways
  6. 初学QT遇到的“_on_OK_clicked(bool)未定义的引用”的问题,以及使用windows远程桌面登录树莓派
  7. CUDA 9.0安装+CUDA版本转换 + cuDNN7.1安装
  8. ObjectDetecionAPI TypeError: __new__() got an unexpected keyword argument 'serialized_options'
  9. 一.对ThreadLocal的理解
  10. Linux 攻击防护基础排查