OLEDB存取BLOB型數(shù)據(jù)


title: OLEDB存取BLOB型數(shù)據(jù)
tags: [OLEDB, 數(shù)據(jù)庫(kù)編程, VC++, 數(shù)據(jù)庫(kù), BLOB型數(shù)據(jù)]
date: 2018-04-20 20:49:37
categories: windows 數(shù)據(jù)庫(kù)編程
keywords: OLEDB, 數(shù)據(jù)庫(kù)編程, VC++, 數(shù)據(jù)庫(kù), 打開(kāi)數(shù)據(jù)源對(duì)象


現(xiàn)代數(shù)據(jù)庫(kù)系統(tǒng)除了支持一些標(biāo)準(zhǔn)的通用數(shù)據(jù)類(lèi)型以外小槐,大多數(shù)還支持一種稱之為BLOB型的數(shù)據(jù)群发。
BLOB全稱為big large object bytes, 大二進(jìn)制對(duì)象類(lèi)型,這種類(lèi)型的數(shù)據(jù)通常用于存儲(chǔ)文檔刘急、圖片、音頻等文件,這些文件一般體積較大,保存這些文件可以很方便的管理和檢索這類(lèi)信息朗徊。在MS SQLSERVER中常見(jiàn)的BLOB數(shù)據(jù)類(lèi)型有text、ntext(n表示unicode)偎漫、image爷恳、nvarchar、varchar骑丸、varbinary等。其中image基本可以用來(lái)保存一切二進(jìn)制文件妒貌,比如word通危、Excel、音頻灌曙、視頻等等類(lèi)型菊碟。
針對(duì)BLOB型數(shù)據(jù),OLEDB也提供了對(duì)它的支持

使用BLOB型數(shù)據(jù)的利弊

一般數(shù)據(jù)庫(kù)對(duì)BLOB型數(shù)據(jù)有特殊的處理方式在刺,比如壓縮等等逆害,在數(shù)據(jù)庫(kù)中存儲(chǔ)BLOB數(shù)據(jù)可以方便的進(jìn)行檢索,展示蚣驼,備份等操作魄幕。但是由于BLOB型數(shù)據(jù)本身比較大,存儲(chǔ)量太大時(shí)數(shù)據(jù)量太大容易拖慢數(shù)據(jù)庫(kù)性能颖杏,所以一般的說(shuō)法都是盡量不要在數(shù)據(jù)庫(kù)中存儲(chǔ)這類(lèi)信息纯陨。特別是圖片,音視頻留储。針對(duì)這類(lèi)文件一般的做法是將其保存在系統(tǒng)的某個(gè)路徑鐘中翼抠,而在數(shù)據(jù)庫(kù)中存儲(chǔ)對(duì)應(yīng)的路徑

操作BLOB型數(shù)據(jù)的一般方法

一般針對(duì)BLOB不能像普通數(shù)據(jù)那樣操作,而需要一些特殊的操作获讳,在OLEDB中通過(guò)設(shè)置綁定結(jié)構(gòu)中的一些特殊值最終指定獲取BLOB型數(shù)據(jù)的一個(gè)ISequentialStream接口指針,最終會(huì)通過(guò)這個(gè)接口來(lái)進(jìn)行BLOB型數(shù)據(jù)的讀寫(xiě)操作

判斷一個(gè)列是否是BLOB型數(shù)據(jù)

判斷某個(gè)列是否是BLOB型數(shù)據(jù)一般通過(guò)如下兩個(gè)條件:

  1. pColumnInfo[i].wType == DBTYPE_IUNKNOW : 包含當(dāng)列信息的DBCOLUMNSINFO 結(jié)構(gòu)體對(duì)象的wType值為DBTYPE_IUNKNOW阴颖,該列的類(lèi)型為DBTYPE_IUNKNOW,該條件也被稱為列類(lèi)型判定
  2. pColumnInfo[i].dwFlags & DBCOLUMNFLAGS_ISLONG :當(dāng)列信息中的dwFlag值為DBCOLUMNFLAGS_ISLONG,也就是說(shuō)該列的標(biāo)識(shí)中包含DBCOLUMNFLAGS_ISLONG屬性丐膝,該判定條件也被稱之為列標(biāo)識(shí)判定
    當(dāng)這兩個(gè)條件之一成立之時(shí)量愧,我們就可以斷定這列為BLOB型數(shù)據(jù)

BLOG型數(shù)據(jù)的綁定

在進(jìn)行BLOB型數(shù)據(jù)的綁定也有特殊要求钾菊,主要體現(xiàn)在下面幾點(diǎn):

  1. 綁定結(jié)構(gòu)的cbMaxLength 需要設(shè)置為0
  2. 綁定結(jié)構(gòu)的wType設(shè)置為DBTYPE_IUNKNOW
  3. 為結(jié)構(gòu)的pObject指針?lè)峙鋬?nèi)存,大小等于DBOBJECT結(jié)構(gòu)的大小
  4. 指定pObject的成員
    pObject->iid = IID_ISequentialStream
    pObject->dwFlags = STGM_READ
  5. 為行緩沖長(zhǎng)度加上一個(gè)IStream指針的長(zhǎng)度侠畔,此時(shí)數(shù)據(jù)源不再提供查詢到的數(shù)據(jù)而提供一個(gè)接口指針结缚,后續(xù)對(duì)BLOB數(shù)據(jù)的操作都使用該指針進(jìn)行
    最后使用完后記得釋放pObject所指向的內(nèi)存空間

讀取BLOB數(shù)據(jù)

根據(jù)前面所說(shuō)的創(chuàng)建綁定結(jié)構(gòu),并為綁定結(jié)構(gòu)賦值软棺,最終可以從結(jié)果集中獲取到一個(gè)ISequentialStream接口指針红竭。調(diào)用接口的Read方法可以讀取到BLOB列中的數(shù)據(jù),而B(niǎo)LOB數(shù)據(jù)的長(zhǎng)度存儲(chǔ)在綁定時(shí)指定的數(shù)據(jù)長(zhǎng)度內(nèi)存偏移處,這與普通列的長(zhǎng)度存放返回方式是一樣的喘落,一般BLOB數(shù)據(jù)都比較長(zhǎng)茵宪,這個(gè)時(shí)候就需要分段讀取。
在使用ISequentialStream接口操作BLOB型數(shù)據(jù)時(shí)需要注意的一個(gè)問(wèn)題是瘦棋,有的數(shù)據(jù)庫(kù)不支持在一個(gè)訪問(wèn)器中訪問(wèn)多個(gè)BLOB數(shù)據(jù)列稀火。一般BLOB數(shù)據(jù)列及其的消耗資源,并且數(shù)據(jù)庫(kù)鼓勵(lì)我們?cè)谠O(shè)計(jì)數(shù)據(jù)庫(kù)表結(jié)構(gòu)的時(shí)候做到一行只有一列BLOB數(shù)據(jù)赌朋,因此很多數(shù)據(jù)庫(kù)并不支持在一個(gè)訪問(wèn)器中讀取多個(gè)BLOB數(shù)據(jù)凰狞。
要判斷數(shù)據(jù)庫(kù)是否支持在一個(gè)訪問(wèn)器中讀取多個(gè)BLOB數(shù)據(jù),可以獲取DBPROP_MULTIPLESTORAGEOBJECTS屬性沛慢,該屬性屬于屬性集DBPROPSET_ROWSET赡若,它是一個(gè)只讀屬性,如果該屬性的值為T(mén)RUE表示支持团甲,為FALSE表示不支持逾冬。
下面是一個(gè)讀取BLOB型數(shù)據(jù)的例子,數(shù)據(jù)庫(kù)中的表結(jié)構(gòu)為:id(int)、text(image)躺苦、png(image)身腻、jpg(image)

void ReadBLOB(IRowset *pIRowset)
{
    COM_DECLARE_INTERFACE(IColumnsInfo);
    COM_DECLARE_INTERFACE(IAccessor);

    DBORDINAL cColumns = 0;
    DBCOLUMNINFO* rgColumnsInfo = NULL;
    LPOLESTR lpszColumnsName = NULL;
    DBBINDING* rgBindings = NULL;
    DBBINDING** ppBindings = NULL; //綁定結(jié)構(gòu)數(shù)組
    DWORD *puDataLen = NULL; //當(dāng)前訪問(wèn)器所需內(nèi)存大小
    DWORD *pulColCnt = NULL; //當(dāng)前訪問(wèn)器中包含的項(xiàng)
    ULONG ulBindCnt = 0; //訪問(wèn)器的數(shù)量
    ULONG uBlob = 0; //當(dāng)前有多少blob數(shù)據(jù)
    HACCESSOR* phAccessor = NULL;
    HROW* hRow = NULL;
    DBCOUNTITEM ulGetRows = 0;
    ULONG uCols = 0;

    PVOID pData1 = NULL; //第1個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖
    PVOID pData2 = NULL; //第2個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖
    PVOID pData3 = NULL; //第3個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖

    HRESULT hRes = pIRowset->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo);
    COM_SUCCESS(hRes, _T("查詢接口pIColumnsInfo失敗,錯(cuò)誤碼為:%08x\n"), hRes);
    hRes = pIColumnsInfo->GetColumnInfo(&cColumns, &rgColumnsInfo, &lpszColumnsName);
    COM_SUCCESS(hRes, _T("獲取結(jié)果集列信息失敗匹厘,錯(cuò)誤碼為:%08x\n"), hRes);

    ppBindings = (DBBINDING**)COM_ALLOC(sizeof(DBBINDING*));
    rgBindings = (DBBINDING*)COM_ALLOC(sizeof(DBBINDING) * cColumns);
    pulColCnt = (DWORD*)COM_ALLOC(sizeof(DWORD));
    puDataLen =  (DWORD*)COM_ALLOC(sizeof(DWORD));
    for (int i = 0; i < cColumns; i++)
    {
    //如果當(dāng)前訪問(wèn)器對(duì)應(yīng)的綁定結(jié)構(gòu)的數(shù)組的首地址為空,將當(dāng)前綁定結(jié)構(gòu)指針作為綁定結(jié)構(gòu)數(shù)組的首地址
        if (NULL == ppBindings[ulBindCnt])
        {
            ppBindings[ulBindCnt] = &rgBindings[i];
        }

        ++pulColCnt[ulBindCnt];
        rgBindings[i].bPrecision = rgColumnsInfo[i].bPrecision;
        rgBindings[i].bScale = rgBindings[i].bScale;
        rgBindings[i].cbMaxLen = 10 * sizeof(WCHAR);
        rgBindings[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
        rgBindings[i].dwPart = DBPART_LENGTH | DBPART_STATUS | DBPART_VALUE;
        rgBindings[i].eParamIO = DBPARAMIO_NOTPARAM;
        rgBindings[i].iOrdinal = rgColumnsInfo[i].iOrdinal;
        rgBindings[i].obStatus = puDataLen[ulBindCnt];
        rgBindings[i].obLength = puDataLen[ulBindCnt] + sizeof(DBSTATUS);
        rgBindings[i].obValue = rgBindings[i].obLength + sizeof(ULONG);
        rgBindings[i].wType = DBTYPE_WSTR;

        if (rgColumnsInfo[i].wType == DBTYPE_IUNKNOWN ||
            rgColumnsInfo[i].dwFlags & DBCOLUMNFLAGS_ISLONG)
        {
            rgBindings[i].cbMaxLen = 0;
            rgBindings[i].wType = DBTYPE_IUNKNOWN;
            rgBindings[i].pObject = (DBOBJECT*)COM_ALLOC(sizeof(DBOBJECT));
            rgBindings[i].pObject->iid = IID_ISequentialStream;
            rgBindings[i].pObject->dwFlags = STGM_READ;
            uBlob++;
        }

        //記錄下每個(gè)訪問(wèn)器所需內(nèi)存的大小
        puDataLen[ulBindCnt] = rgBindings[i].obValue + rgBindings[i].cbMaxLen;
        if (rgBindings[i].wType == DBTYPE_IUNKNOWN)
        {
            puDataLen[ulBindCnt] = rgBindings[i].obValue + sizeof(ISequentialStream*);
        }

        puDataLen[ulBindCnt] = UPGROUND(puDataLen[ulBindCnt]);
        //判斷當(dāng)前是否需要?jiǎng)?chuàng)建單獨(dú)的訪問(wèn)器
        if ((uBlob || rgBindings[i].iOrdinal == 0))
        {
            ulBindCnt++;
            ppBindings = (DBBINDING**)COM_REALLOC(ppBindings, sizeof(DBBINDING*) * (ulBindCnt + 1));
            puDataLen = (DWORD*)COM_REALLOC(puDataLen, sizeof(DWORD) * (ulBindCnt + 1));
            pulColCnt = (DWORD*)COM_REALLOC(pulColCnt, sizeof(DWORD) * (ulBindCnt + 1));
        }
    }

    //創(chuàng)建訪問(wèn)器
    phAccessor = (HACCESSOR*)COM_ALLOC( (ulBindCnt + 1) * sizeof(HACCESSOR));
    hRes = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
    COM_SUCCESS(hRes, _T("查詢IAccessor接口失敗,錯(cuò)誤碼為:%08x\n"), hRes);
    for (int i = 0; i < ulBindCnt; i++)
    {
        hRes = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pulColCnt[i], ppBindings[i], 0, &phAccessor[i], NULL);
        COM_SUCCESS(hRes, _T("創(chuàng)建訪問(wèn)器失敗扰路,錯(cuò)誤碼為:%08x\n"), hRes);
    }

    //讀取其中的一行數(shù)據(jù)
    hRes = pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &ulGetRows, &hRow);
    COM_SUCCESS(hRes, _T("讀取行數(shù)據(jù)失敗尤溜,錯(cuò)誤碼為:%08x\n"), hRes);

    //讀取第一個(gè)綁定結(jié)構(gòu)中的信息
    pData1 = COM_ALLOC(puDataLen[0]);
    hRes = pIRowset->GetData(hRow[0], phAccessor[0], pData1);
    for(int i = 0; i < pulColCnt[0]; i++)
    {
        if (ppBindings[0][i].wType == DBTYPE_IUNKNOWN)
        {
            DBSTATUS dbStatus = *(DBSTATUS*)((BYTE*)pData1 + ppBindings[0][i].obStatus);
            if (dbStatus == DBSTATUS_S_OK)
            {
                ULONG uFileLen = *(ULONG*)((BYTE*)pData1 + ppBindings[0][i].obLength);
                if (uFileLen > 0)
                {
                    DWORD dwReaded = 0;
                    PVOID pFileData = COM_ALLOC(uFileLen);
                    ZeroMemory(pFileData, uFileLen);
                    ISequentialStream  *pSeqStream = *(ISequentialStream**)((BYTE*)pData1 + ppBindings[0][i].obValue);
                    pSeqStream->Read(pFileData, uFileLen, &dwReaded);

                    WriteFileData(_T("1.txt"), pFileData, dwReaded);
                }
            }
        }
    }

    //后續(xù)的部分就不再寫(xiě)出來(lái)了,寫(xiě)法與上面的代碼類(lèi)似
    pIRowset->ReleaseRows(1, hRow, NULL, NULL, NULL);
__CLEAR_UP:
  //后面是清理的代碼

由于我們事先知道數(shù)據(jù)表的結(jié)構(gòu)汗唱,它有3個(gè)BLOB型數(shù)據(jù),所以這里直接定義了3個(gè)緩沖用來(lái)接收3個(gè)BLOB型數(shù)據(jù)哩罪。為了方便檢測(cè)授霸,我們另外寫(xiě)了一個(gè)的函數(shù)巡验,將讀取出來(lái)的BLOB數(shù)據(jù)寫(xiě)入到文件中碘耳,事后以文件顯示是否正確來(lái)測(cè)試這段代碼
首先還是與以前一樣,獲取數(shù)據(jù)表的結(jié)構(gòu)捕捂,然后進(jìn)行綁定斗搞,注意這里由于使用的是SQL Server,它不支持一個(gè)訪問(wèn)器中訪問(wèn)多個(gè)BLOB允悦,所以這里沒(méi)有判斷直接綁定不同的訪問(wèn)器虑啤。
在綁定的時(shí)候使用ulBindCnt作為當(dāng)前訪問(wèn)器的數(shù)量,在循環(huán)里面有一個(gè)判斷當(dāng)(uBlob || rgBindings[i].iOrdinal == 0) && (ulBindCnt != cColumns - 1)條件成立時(shí)將訪問(wèn)器的數(shù)量加1全闷,該條件表示之前已經(jīng)有blob型數(shù)據(jù)(之前SQL不支持一個(gè)訪問(wèn)器訪問(wèn)多個(gè)BLOB铣墨,如果之前已經(jīng)有BLOB數(shù)據(jù)了伊约,就需要另外創(chuàng)建訪問(wèn)器)或者當(dāng)前是第0行(因?yàn)榈?行只允許讀孕蝉,所以將其作為與BLOB型數(shù)據(jù)一樣處理)降淮,當(dāng)這些條件成立時(shí)會(huì)新增一個(gè)訪問(wèn)器超埋,而隨著訪問(wèn)器的增加佳鳖,需要改變ppBindings數(shù)組中的元素,該數(shù)組存儲(chǔ)的是訪問(wèn)器對(duì)應(yīng)的綁定結(jié)構(gòu)開(kāi)始的指針来庭。數(shù)組puDataLen表示的是當(dāng)前訪問(wèn)器所需內(nèi)存的大小穿挨,pulColCnt表示當(dāng)前訪問(wèn)器中共有多少列,針對(duì)這個(gè)表最終這些結(jié)構(gòu)的內(nèi)容大致如下圖:


各個(gè)數(shù)組元素圖

綁定完成之后菜皂,后面就是根據(jù)數(shù)組中的內(nèi)容創(chuàng)建對(duì)應(yīng)的訪問(wèn)器厉萝,然后綁定冀泻、讀取數(shù)據(jù),針對(duì)BLOB數(shù)據(jù)胳施,我們還是一樣從對(duì)應(yīng)緩沖的obValue偏移處得到接口指針肢专,然后調(diào)用接口的Read方法讀取博杖,最后寫(xiě)入文件

BLOB數(shù)據(jù)的寫(xiě)入:

要寫(xiě)入BLOB型數(shù)據(jù)也需要使用ISequentialStream接口,但是它不像之前可以直接使用接口的Write方法,寫(xiě)入的對(duì)象必須要自己從ISequentialStream接口派生哩盲,并指定一段內(nèi)存作為緩沖狈醉,以便供OLEDB組件調(diào)用寫(xiě)方法時(shí)作為數(shù)據(jù)緩沖。這段緩沖必須要保證分配在COM堆上抒线,也就是要使用CoTaskMemory分配內(nèi)存渣慕。
這里涉及到的對(duì)象主要有IStream逊桦、ISequentialStream、IStorage睡陪、ILockBytes夕凝,同樣,并不是所有數(shù)據(jù)源都支持這4類(lèi)對(duì)象逮矛,具體支持哪些可以查詢DBPROPSET_DATASOURCEINFO屬性集中的DBPROP_STRUCTUREDSTORAGE屬性來(lái)判定须鼎,目前SQL Server中支持ISequentialStream接口。
雖然我們可以使用這種方式來(lái)實(shí)現(xiàn)讀寫(xiě)B(tài)LOB汞窗,但是每種數(shù)據(jù)源支持的程度不同赡译,而且有的數(shù)據(jù)源甚至不支持這種方式蝌焚,為了查詢對(duì)讀寫(xiě)B(tài)LOB數(shù)據(jù)支持到何種程度,可以查詢DBPROPSET_DATASOURCEINFO屬性集合的DBPROP_OLEOBJECTS屬性來(lái)判定
通常有以下幾種支持方式(DBPROP_OLEOBJECTS屬性的值,按位設(shè)置):

  1. DBPROPVAL_OO_BLOB: 就是之前介紹的接口方式许帐,使用接口的方式來(lái)讀寫(xiě)B(tài)LOB數(shù)據(jù)
    DBPROPVAL_OO_DIRECTBIND: 可以直接綁定在行中,通過(guò)行訪問(wèn)器像普通列一樣訪問(wèn)毕谴,也就是說(shuō)它不需要獲取專門(mén)的指針來(lái)操作涝开,他可以就像操作普通數(shù)據(jù)那樣,分配對(duì)應(yīng)內(nèi)存就可以訪問(wèn)惧浴,但是要注意分配內(nèi)存的大小奕剃,每行中對(duì)應(yīng)列中BLOB的數(shù)據(jù)長(zhǎng)度差別可能會(huì)很明顯纵朋,比如有的可能是一部長(zhǎng)達(dá)2小時(shí)的電影文件茄袖,而有的可能是一部短視頻宪祥,它們之間的差距可能會(huì)達(dá)到上G家乘,而按照最小的來(lái)可能會(huì)發(fā)生截?cái)嗖匕模醋畲蟮姆峙淇赡軙?huì)發(fā)生多達(dá)好幾個(gè)G的內(nèi)存浪費(fèi)
    DBPROPVAL_OO_IPERSIST:通過(guò)IPersistStream, IPersistStreamInit, or IPersistStorage三個(gè)接口的Persist對(duì)象訪問(wèn)
    DBPROPVAL_OO_ROWOBJECT: 支持整行作為一個(gè)對(duì)象來(lái)訪問(wèn),通過(guò)結(jié)果集對(duì)象的IGetRow接口來(lái)獲得行對(duì)象翔悠,但是這種模式會(huì)破壞第三范式,所以一般數(shù)據(jù)庫(kù)都不支持
    DBPROPVAL_OO_SCOPED: 通過(guò)IScopedOperations接口來(lái)暴露行對(duì)象,通過(guò)這個(gè)接口可以暴露一個(gè)樹(shù)形的結(jié)果集對(duì)象
    DBPROPVAL_OO_SINGLETON: 直接通過(guò)ICommand::Execute和IOpenRowset::OpenRowset來(lái)打開(kāi)行對(duì)象
    下面是插入BLOB數(shù)據(jù)的一個(gè)實(shí)例
//自定義一個(gè)
class CSeqStream : public ISequentialStream
{
public:
    // Constructors
    CSeqStream();
    virtual ~CSeqStream();
public:
    virtual BOOL Seek(ULONG iPos); //將當(dāng)前內(nèi)存指針偏移到指定位置
    virtual BOOL CompareData(void* pBuffer); //比較兩段內(nèi)存中的值
    virtual ULONG Length()
    {
        return m_cBufSize;
    };
    virtual operator void* const()
    {
        return m_pBuffer;
    };
public:
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv);

  //讀寫(xiě)內(nèi)存的操作,這些是必須實(shí)現(xiàn)的函數(shù)
    STDMETHODIMP Read(
        /* [out] */ void __RPC_FAR *pv,
        /* [in]  */ ULONG cb,
        /* [out] */ ULONG __RPC_FAR *pcbRead);

    STDMETHODIMP Write(
        /* [in] */ const void __RPC_FAR *pv,
        /* [in] */ ULONG cb,
        /* [out]*/ ULONG __RPC_FAR *pcbWritten);
private:
    ULONG m_cRef;       // reference count
    void* m_pBuffer;    // buffer
    ULONG m_cBufSize;   // buffer size
    ULONG m_iPos;       // current index position in the buffer
};
//插入數(shù)據(jù)第一列BLOB數(shù)據(jù)
    //這里由于已經(jīng)事先知道每列的數(shù)據(jù)結(jié)構(gòu)妇斤,因此采用偷懶的方法丹拯,一行行的插入
    pData1 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pdwDataLen[nCol]);
    for(int i = 0; i < pulColCnt[nCol]; i++)
    {
        if (DBTYPE_IUNKNOWN == ppBindings[nCol][i].wType)
        {
            *(DBSTATUS*)((BYTE*)pData1 + ppBindings[nCol][i].obStatus) = DBSTATUS_S_OK;
            CSeqStream *pSeqStream = new CSeqStream();
            GetFileData(_T("test.txt"), dwFileLen, pFileData);
            pSeqStream->Write(pFileData, dwFileLen, &dwWritten);
            pSeqStream->Seek(0); //寫(xiě)這個(gè)操作將緩存的指針偏移到了最后咽笼,需要調(diào)整一下,以便OLEDB組件在插入BLOB數(shù)據(jù)時(shí)從緩存中讀取
            HeapFree(GetProcessHeap(), 0, pFileData);
            *(ULONG*)((BYTE*)pData1 + ppBindings[nCol][i].obLength) = dwFileLen;
            *(ISequentialStream**)((BYTE*)pData1 + ppBindings[nCol][i].obValue) = pSeqStream;
            //此處不用release pSeqStream媳纬,COM組件會(huì)自動(dòng)釋放
        }else
        {
            //根據(jù)數(shù)據(jù)庫(kù)定義钮惠,此處應(yīng)該為ID
            *(ULONG*)((BYTE*)pData1 + ppBindings[nCol][i].obLength) = 10;
            if (DBTYPE_WSTR == ppBindings[nCol][i].wType)
            {
                StringCchCopy((LPOLESTR)((BYTE*)pData1 + ppBindings[nCol][i].obValue), 10, SysAllocString(OLESTR("1")));
            }
        }
    }

    hRes = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, phAccessor[nCol], pData1, &hNewRow);
    COM_SUCCESS(hRes, _T("插入第1列BLOB數(shù)據(jù)失敗七芭,錯(cuò)誤碼為:%08x\n"), hRes);

在上面的代碼中首先定義一個(gè)派生類(lèi)狸驳,用來(lái)進(jìn)行BLOB數(shù)據(jù)的讀寫(xiě),然后在后面的代碼中演示了如何使用它
在后面的一段代碼中撰糠,基本步驟和之前一樣辩昆,經(jīng)過(guò)連接數(shù)據(jù)源、創(chuàng)建回話對(duì)象术辐,打開(kāi)表辉词,然后綁定,獲取行訪問(wèn)器隧魄,這里由于代碼基本不變隘蝎,為了節(jié)約篇幅所以省略它們嘱么,只貼出最重要的部分。
在插入的代碼中几迄,首先查找訪問(wèn)器中的各個(gè)列的屬性冰评,如果是BLOB數(shù)據(jù)就采用BLOB數(shù)據(jù)的插入辦法甲雅,否則用一般數(shù)據(jù)的插入辦法。插入BLOB數(shù)據(jù)時(shí)弛姜,首先創(chuàng)建一個(gè)派生類(lèi)的對(duì)象妖枚,注意此處由于后續(xù)要交給OLEDB組件調(diào)用绝页,所以不能用棧內(nèi)存。我們先調(diào)用類(lèi)的Write方法將內(nèi)存寫(xiě)入對(duì)應(yīng)的緩沖中结啼,然后調(diào)用Seek函數(shù)將內(nèi)存指針偏移到緩沖的首地址屈芜,這個(gè)指針的作用就相當(dāng)于文件的文件指針井佑,COM組件在調(diào)用對(duì)應(yīng)函數(shù)將它插入數(shù)據(jù)庫(kù)時(shí)會(huì)采用這個(gè)內(nèi)存的指針,所以必須將其置到首地址處焦蘑。讓后將對(duì)象的指針?lè)湃氲綄?duì)應(yīng)的obvalues偏移中例嘱,設(shè)置對(duì)應(yīng)的數(shù)據(jù)大小為BLOB數(shù)據(jù)的大小宁舰,最后只要像普通數(shù)據(jù)類(lèi)型那樣調(diào)用對(duì)應(yīng)的更新方法即可實(shí)現(xiàn)BLOB數(shù)據(jù)的插入

最后貼上兩個(gè)例子的詳細(xì)代碼地址
示例1:BLOB數(shù)據(jù)的讀取
示例2:BLOB數(shù)據(jù)的插入

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛮艰,一起剝皮案震驚了整個(gè)濱河市壤蚜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聪富,老刑警劉巖著蟹,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件草则,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡炕横,警方通過(guò)查閱死者的電腦和手機(jī)份殿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)卿嘲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沃疮,你說(shuō)我怎么就攤上這事∫厍眩” “怎么了俊啼?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵授帕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我泉手,道長(zhǎng)偶器,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮霎苗,結(jié)果婚禮上唁盏,老公的妹妹穿的比我還像新娘。我一直安慰自己昆淡,他們只是感情好刽严,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布舞萄。 她就那樣靜靜地躺著倒脓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甘晤。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,245評(píng)論 1 299
  • 那天艇炎,我揣著相機(jī)與錄音,去河邊找鬼居砖。 笑死驴娃,一個(gè)胖子當(dāng)著我的面吹牛唇敞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咒精,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼模叙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鞋屈!你這毒婦竟也來(lái)了厂庇?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎炼杖,沒(méi)想到半個(gè)月后坤邪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怎静,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年蚓聘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片与纽。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡急迂,死狀恐怖蹦肴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情勺阐,我是刑警寧澤渊抽,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布背零,位于F島的核電站徙瓶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏灵疮。R本人自食惡果不足惜壳繁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一闹炉、第九天 我趴在偏房一處隱蔽的房頂上張望蒿赢。 院中可真熱鬧,春花似錦渣触、人聲如沸羡棵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)皂冰。三九已至店展,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秃流,已是汗流浹背赂蕴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留概说,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓峻贮,卻偏偏與公主長(zhǎng)得像席怪,于是被迫代替她去往敵國(guó)和親应闯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子纤控,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容