任务描述:
给定dmp文件和表结构,要求导出其中blob字段的图片,并以其他若干字段组合作为图片文件名。
解决步骤:
1、从 oracle 官网下载 11g 服务端版和客户端版。
2、在服务器上安装 服务端版,并导入 dmp 文件(从10g中导出。)。在控制台中使用imp命令,输入用户名/密码,除了 “要求dmp文件的地方要输入dmp文件的路径文件名” 和 “最后一个选项是否要导入整个dmp文件,输入yes” 外,一路回车即可。如果导入过程有什么错误,做相应修改。比如提示未找到表空间,就另起一个 SQL*PLUS 建一个表空间。
3、在开发机器上安装 客户端版,用 VC + ADO 导出数据。其中有几个需注意的地方。
a、_ConectionPtr对象->Open方法,连接串中的 Data Source 不能只是服务器的 ip,还需要数据库名,应该是 "//192.168.1.11/orcl" 这样的形式。
b、FieldPtr对象->GetItem方法,如果使用索引,要转成 long 或 short 型,不能用 int 型。(这个令人崩溃的BUG 浪费了我很多时间。)
c、_RecordsetPtr对象->Open方法,如果SQL语句比较复杂,比较使用 Left Join关联两个表,最后的";"就要去掉,否则有 ORA-00911 的错误。
代码:
AdoDb.h
/*
-------------------------------------------------------
类 名: CAdoDb
文件名: AdoDb.h / cpp
描 述: 数据库操作。
备 注: wujp 2013-07-18 初次创建。
-------------------------------------------------------
*/
#pragma once
#include <windows.h>
#import "C:\\Program Files\\Common Files\\System\\ado\\msado15.dll" no_namespace rename("EOF", "EndOfFile")
class CAdoDb
{
public:
CAdoDb();
virtual ~CAdoDb();
public:
enum
{
MAX_FIELD_NAME_LEN = 100,
MAX_FIELD_TEXT_LEN = 200,
};
public:
BOOL Connenct(const char* pcszHostName, const char* pcszDbName, const char* pcszUserName, const char* pcszPassword);
//给定 表名 和 字段名 来获取记录集。
BOOL PrepareRecordset(const char* pcszTableName, const char arrcszFieldName[][MAX_FIELD_NAME_LEN], int nFieldCount);
//给定 SQL语句 来获取记录集。
BOOL PrepareRecordset(const char* pcszRecordsetSql);
//只考虑记录中只有一个 blob字段 的情况。
BOOL GetCurrentRecord(char arrszFieldValue[][MAX_FIELD_TEXT_LEN], int nFieldCount, BYTE** ppbyBlob, int* pnBlobSize);
void CloseRecordset();
protected:
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);
protected:
_ConnectionPtr m_ptrConnect;
_RecordsetPtr m_ptrRecordset;
};
AdoDb.cpp
#include "AdoDb.h"
CAdoDb::CAdoDb()
{
CoInitialize(NULL);
}
CAdoDb::~CAdoDb()
{
if (m_ptrRecordset)
{
if (m_ptrRecordset->GetState() != adStateClosed)
m_ptrRecordset->Close();
m_ptrRecordset.Release();
}
if (m_ptrConnect)
{
if (m_ptrConnect->GetState() != adStateClosed)
m_ptrConnect->Close();
m_ptrConnect.Release();
}
CoUninitialize();
}
BOOL CAdoDb::Connenct(const char* pcszHostName, const char* pcszDbName, const char* pcszUserName, const char* pcszPassword)
{
if (!m_ptrConnect)
m_ptrConnect.CreateInstance(__uuidof(Connection));
if (!m_ptrRecordset)
m_ptrRecordset.CreateInstance(__uuidof(Recordset));
if (!m_ptrConnect || !m_ptrRecordset)
return FALSE;
try
{
char szConn[1000];
sprintf_s(szConn, sizeof(szConn), "Provider = 'OraOLEDB.Oracle.1';Data Source = '%s/%s';User ID = '%s';Password = '%s';", pcszHostName, pcszDbName, pcszUserName, pcszPassword);
m_ptrConnect->Open(szConn, "", "", adConnectUnspecified);
}
catch(_com_error& e)
{
PrintProviderError(m_ptrConnect);
PrintComError(e);
return FALSE;
}
return TRUE;
}
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms681557(v=vs.85).aspx
void CAdoDb::PrintProviderError(_ConnectionPtr pConnection)
{
// Print Provider Errors from Connection object.
// pErr is a record object in the Connection's Error collection.
ErrorPtr pErr = NULL;
if ( (pConnection->Errors->Count) > 0) {
long nCount = pConnection->Errors->Count;
// Collection ranges from 0 to nCount -1.
for ( long i = 0 ; i < nCount ; i++ ) {
pErr = pConnection->Errors->GetItem(i);
//printf("\t Error number: %x\t%s", pErr->Number, pErr->Description);
char szInfo[500];
sprintf_s(szInfo, sizeof(szInfo), "Error number = %x\r\nDesc = %s", pErr->Number, (const char*)pErr->Description);
MessageBox(NULL, szInfo, "ADO错误", MB_OK);
}
}
}
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms681557(v=vs.85).aspx
void CAdoDb::PrintComError(_com_error &e)
{
/*
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
// Print COM errors.
printf("Error\n");
printf("\tCode = %08lx\n", e.Error());
printf("\tCode meaning = %s\n", e.ErrorMessage());
printf("\tSource = %s\n", (LPCSTR) bstrSource);
printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
*/
char szInfo[2000];
sprintf_s(szInfo, sizeof(szInfo), "Code = %08lx\r\nCode meaning = %s\r\nSource = %s\r\nDescription = %s",
e.Error(), e.ErrorMessage(), (const char*)_bstr_t(e.Source()), (const char*)_bstr_t(e.Description()));
MessageBox(NULL, szInfo, "COM错误", MB_OK);
return;
}
BOOL CAdoDb::PrepareRecordset(const char* pcszTableName, const char arrcszFieldName[][MAX_FIELD_NAME_LEN], int nFieldCount)
{
if (nFieldCount < 1)
return FALSE;
try
{
//连接字段名,按逗号分隔。
char szFieldNameString[1000];
memset(szFieldNameString, 0, sizeof(szFieldNameString));
strcpy_s(szFieldNameString, sizeof(szFieldNameString), arrcszFieldName[0]);
int i;
for (i=1; i<nFieldCount; i++)
{
strcat_s(szFieldNameString, sizeof(szFieldNameString), ",");
strcat_s(szFieldNameString, sizeof(szFieldNameString), arrcszFieldName[i]);
}
char szSql[1000];
sprintf_s(szSql, sizeof(szSql), "select %s from %s", szFieldNameString, pcszTableName);
m_ptrRecordset->Open(szSql, m_ptrConnect.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText);
m_ptrRecordset->MoveFirst();
}
catch(_com_error& e)
{
PrintComError(e);
return FALSE;
}
return TRUE;
}
BOOL CAdoDb::PrepareRecordset(const char* pcszRecordsetSql)
{
try
{
m_ptrRecordset->Open(pcszRecordsetSql, m_ptrConnect.GetInterfacePtr(), adOpenStatic, adLockOptimistic, adCmdText);
m_ptrRecordset->MoveFirst();
}
catch(_com_error& e)
{
PrintComError(e);
return FALSE;
}
return TRUE;
}
void CAdoDb::CloseRecordset()
{
m_ptrRecordset->Close();
}
BOOL CAdoDb::GetCurrentRecord(char arrszFieldValue[][MAX_FIELD_TEXT_LEN], int nFieldCount, BYTE** ppbyBlob, int* pnBlobSize)
{
if (m_ptrRecordset->EndOfFile)
return FALSE;
try
{
//指向当前记录的字段集。
FieldsPtr ptrFields = m_ptrRecordset->GetFields();
if (nFieldCount != ptrFields->GetCount())
return FALSE;
int i;
for (i=0; i<nFieldCount; i++)
{
//这里一定要先转成 long 或 short。
//因为如果是 int,_variant_t 的 vt 值就是 VT_INT,值保存在 intVal。
//而 long/short 的 vt类型是 VT_I4/VT_I2,值保存在 lVal/iVal。
FieldPtr ptrField = ptrFields->GetItem(_variant_t((long)i));
//字段长度,没什么用,随便看看。
long lDataSize = ptrField->GetActualSize();
//字段类型,没什么用,随便看看。
DataTypeEnum nType = ptrField->GetType();
//字段名,没什么用,随便看看。
_bstr_t bstrName = ptrField->GetName();
if (ptrField->GetAttributes() & adFldLong)
{
//这是一个 blob 字段。
long lDataSize = ptrField->GetActualSize();
_variant_t varData = ptrField->GetChunk(lDataSize);
if (pnBlobSize)
{
*pnBlobSize = lDataSize;
*ppbyBlob = new BYTE[lDataSize];
BYTE* pbyData = NULL;
SafeArrayAccessData(varData.parray, (void**)&pbyData);
memcpy(*ppbyBlob, pbyData, lDataSize);
SafeArrayUnaccessData(varData.parray);
}
//blob 的数据已放入 ppbyBlob 和 pnBlobSize 参数中,这里只是做个标记。
strcpy_s(arrszFieldValue[i], MAX_FIELD_TEXT_LEN, "blob");
}
else
{
_variant_t varValue = ptrField->GetValue();
//如果字段类型不是字符串类型的,_bstr_t 会把它自动转成 BSTR,再复制的。
strcpy_s(arrszFieldValue[i], MAX_FIELD_TEXT_LEN, (const char*)_bstr_t(varValue));
}
}
}
catch(_com_error& e)
{
PrintComError(e);
return FALSE;
}
m_ptrRecordset->MoveNext();
return TRUE;
}