天天看點

使用 ADO 向資料庫中存儲一張圖檔

  【備注】本文中所闡述代碼應用于我為BS架構業務系統開發的某個 ActiveX 控件中。

  我們将向一個典型SQL資料庫中的某表的 Image 類型的字段(假設字段名稱為“PHOTO”)存儲一副圖檔,實際上 Image 字段是一種二進制流,它是由應用程式負責解釋的。是以在這裡我們是将其當作 jpg 圖像檔案。換句話說,把 jpg 檔案的原始位元組流存儲到 Image 字段中去。由于通過記憶體中轉,這種檔案的尺寸不宜過大。

  我們假設在一個CImage對象中已經加載的就是要儲存的圖檔,同時也打開了相應表的一個用于插入記錄的記錄集指針 (_RecordsetPtr )。

  則相關代碼如下:

  

使用 ADO 向資料庫中存儲一張圖檔
使用 ADO 向資料庫中存儲一張圖檔

代碼

//存儲圖檔

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”)。

使用 ADO 向資料庫中存儲一張圖檔
使用 ADO 向資料庫中存儲一張圖檔

代碼_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);