str.Format("SELECT sn,pic,patient_id FROM T_Pacs_Pic WHERE patient_id=/'%s/'",magiccard);
m_pRecordset->Open((_bstr_t)str,_variant_t((IDispatch*)m_pConnection,true),adOpenStatic,adLockOptimistic,adCmdText);
while(!m_pRecordset->adoEOF)
{
//read data
long lDataLength = m_pRecordset->Fields->GetItem("pic")->ActualSize;
varBLOB = m_pRecordset->GetFields()->GetItem("pic")->GetChunk(lDataLength);
if(varBLOB.vt == (VT_ARRAY | VT_UI1))
{
BYTE *pBuf = NULL;
SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);
CString strFileName ;
strFileName.Format("%stmp%d.bmp",theApp.TEMP_PATHNAME,ret);
CFile outFile;
outFile.Open(strFileName,CFile::modeCreate|CFile::modeWrite);
outFile.Write(pBuf,lDataLength);
outFile.Close();
SafeArrayUnaccessData (varBLOB.parray);
ret++;
}
为什么只取得4096字节?如果小于4096的图片却可以存取。
请教高手:
1、如何取得图片大小?
2、如果分段读取怎么做?
3、SYBASE不很熟,看到帮助里面有 READTEXT,怎么用?
4、SYBASE帮助中有这样的语句:SELECT xp_write_file( filename, picture) FROM t1,但是使用的时候提示xp_write_file非内置功能,xp是扩展存储过程,为什么不能用?
Public Function AddPerson(p As person) As Boolean
Dim nAffected As Integer
Dim i As Integer
'先清除原有的参数
Call ClearParameters(m_comm)
'加入当前的参数
m_comm.CommandText = "X_AddCont"
m_comm.CommandType = adCmdStoredProc
m_comm.Parameters.Append m_comm.CreateParameter("@user", adVarChar, adParamInput, 200, strUser)
m_comm.Parameters.Append m_comm.CreateParameter("@name", adVarChar, adParamInput, 200, p.strName)
m_comm.Parameters.Append m_comm.CreateParameter("@sex", adVarChar, adParamInput, 50, p.strSex)
m_comm.Parameters.Append m_comm.CreateParameter("@birth", adVarChar, adParamInput, 50, p.strBirth)
m_comm.Parameters.Append m_comm.CreateParameter("@addr", adVarChar, adParamInput, 200, p.strAddr)
m_comm.Parameters.Append m_comm.CreateParameter("@tel1", adVarChar, adParamInput, 50, p.strTel1)
m_comm.Parameters.Append m_comm.CreateParameter("@tel2", adVarChar, adParamInput, 50, p.strTel2)
m_comm.Parameters.Append m_comm.CreateParameter("@email", adVarChar, adParamInput, 100, p.strEmail)
m_comm.Parameters.Append m_comm.CreateParameter("@page", adVarChar, adParamInput, 100, p.strPage)
m_comm.Parameters.Append m_comm.CreateParameter("@rel", adVarChar, adParamInput, 50, p.strRel)
m_comm.Parameters.Append m_comm.CreateParameter("@img", adVarBinary, adParamInput, 10000000)
Dim strPathPicture As String
Dim DataFile As Integer, FileLen As Long, Chunks As Integer, Fragment As Integer
Dim Chunk() As Byte
Dim ChunkSize As Integer
DataFile = 1
ChunkSize = 1024
Open p.strImg For Binary Access Read As DataFile
FileLen = LOF(DataFile) ' 文件中数据长度
Chunks = FileLen / ChunkSize
Fragment = FileLen Mod ChunkSize
ReDim Chunk(Fragment)
Get DataFile, , Chunk()
m_comm.Parameters("@img").AppendChunk Chunk()
ReDim Chunk(ChunkSize)
For i = 1 To Chunks
Get DataFile, , Chunk()
m_comm.Parameters("@img").AppendChunk Chunk()
Next i
Close DataFile
m_comm.Execute (nAffected)
AddPerson = True
End Function
[总结]向数据库中存取图片(BITMAP)
BITMAP
测试环境:windows xp2 vc6.0 access2003/sql2000
当前时间所限就先把关于BMP文件的操作总结了一下,在接下来会再总结一下其它几种图片在数据库的操作的。
转入下文:
个人感觉向数据库中写入和读取位图,也没有什么难的,重点还是在于对位图文件的读取,如果这一关能把握好的话,那也就没有问题了。如果哪位看过我的一篇《VC下显示位图的几种方法》其中有一段是关于,直接读取位图文件的,如果看明白了它,也今天这几个问题就会很好明白了。
进入正题:既然是向数据库中添加位图信息,那么有必须明白我们的数据是以什么形式存放在数据库的,当然没有别的方法,只能是二进制了,所以在创建数据库的时候,一定要注意选择的字段的类型,不然是添加下进去的。(刚开始的时候,我就遇到了这个问题,费了好长时间才搞定的。Access中的字段定义为” OLE 对象”,sql2000中字义为image就行了。)
1> 读取一个位图,
CFile FilePic;//用来读取位图文件
DWORD FileLen=0;//位图的长度
char* FileBuff;//用于存放位图信息
if(!FilePic.Open(FilePath,Cfile::modeRead))//打开位图文件
{
MessageBox("打开图片信息失败!");
return false;
}
FileLen=FilePic.GetLength();//得到位图的长度
FileBuff=new char[FileLen+1];//给位图文件申请内在空间
memset(FileBuff,0,FileLen+1);//初始化位图文件的空间
if(!FileBuff)//判断位图空间是否申请成功
{
MessageBox("给图片分配空间失败!");
return false;
}
if(FilePic.Read(FileBuff,FileLen)!=FileLen)//读取位图信息,存入到FileBuff中去
{
MessageBox("读取图片信息失败!");
return false;
}
2> 把位图信息存放到VARIANT对象中,存入数据库中
char* pBuff= FileBuff;
VARIANT varPic;//该对象用于存放位图信息
SAFEARRAY *safeArray;//定义一个SAFEARRAY结构对象(对于这个东西,会有专门的文档大家可以看一下,网上很多,不过都差不多,最好去MSDN上找一下)
SAFEARRAYBOUND rgsabound[1];//此结构体用来定义SAFEARRAY的边界,详情见MSDN
if(pBuff)//判断位图文件是否为空
{
rgsabound[0].lLbound=0;//定义下界
rgsabound[0].cElements=DwPic;//定义上限
safeArray=SafeArrayCreate(VT_UI1,1,rgsabound);// 使用SafeArrayCreate在堆上创建一维数组
for(long i=0;i<(long)DwPic;i++)
{
SafeArrayPutElement(safeArray,&i,pBuff++);//传值
}
varPic.vt=VT_ARRAY|VT_UI1;//把值给VARIANT对象
varPic.parray=safeArray; //把值给VARIANT对象
}
3> 把数据写到数据库中
ACCESS2003:
pRecordset->AddNew();
try
{
pRecordset->PutCollect("Name",_variant_t("zhang1"));
pRecordset->GetFields()->GetItem("Picture")->AppendChunk(varPic);
}
catch(_com_error e)
{
MessageBox("Add pic to access is falied!");
}
pRecordset->Update();
sql2000:
pRecordsetSql->AddNew();
try
{
pRecordsetSql->PutCollect("Name",_variant_t("zhang1"));
pRecordsetSql->GetFields()->GetItem("Pic")->AppendChunk(SavePic(FileBuff,FileLen));
}
catch(_com_error e)
{
MessageBox("Add picture to sqlserver2000 is failed!");
}
pRecordsetSql->Update();
在数据库中读取位图信息并显示出来:
其实读取并且显示过程的过程也非常的简单,主要是把它们在数据库中读取出来,放到一个内存空间中,然后把这此些读取出来的信息再转换成一个BITMAP就可以显示了,(如果不明白怎么转换的话,可以看一下关于位图的文件格式,我关于它的一些信息,也可以参与一下本人的《VC下显示位图的几种方法》里面有详细的解释)
进入正题:
_variant_t TheValue;//存储读出来的数据
DWORD DataSize;//在数据库中读取出来的位图的大小
char *pBuff;//用于存放位图的内存空间
CString str="select * from Picture";//SQL语句^_^
BSTR bstrRecordset=str.AllocSysString();//这句也不用解释了吧
HBITMAP hBitmap;//定义一个HBITMAP对象,用于显示位图用
try
{
pRecordset->Open(bstrRecordset,pConnection.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);
pRecordset->MoveLast();
TheValue=pRecordset->GetCollect("Picture");//读取字段一
DataSize=pRecordset->GetFields()->GetItem("Picture")->ActualSize;//得到位图字段的大小
if(DataSize>0)//判断那个位图字段是否为空
{
if(TheValue.vt==(VT_ARRAY|VT_UI1))
{
if(pBuff=new char[DataSize+1])
{
char *buff;
/***********************主要也就下面这几句画,其它的和显示位图相似**************************/
SafeArrayAccessData(TheValue.parray,(void**)&buff);//把位图数据放到buff中去
memcpy(pBuff,buff,DataSize);//把位图数据放到pBuff中
SafeArrayUnaccessData(TheValue.parray);//释放
hBitmap=MemTOPic(pBuff);//这是一个自字义
}
}
}
}
catch(_com_error e)
{
MessageBox("打开数据表失败!");
return ;
}
/******************根据HBITMAP把位图显示出来************************/
CBitmap cBitmap;
CClientDC *pDC=new CClientDC(this);
BITMAP bitmap;
CDC MemDC;
cBitmap.Attach(hBitmap);
MemDC.CreateCompatibleDC(pDC);
MemDC.SelectObject(&cBitmap);
cBitmap.GetBitmap(&bitmap);
pDC->StretchBlt(0,0,bitmap.bmWidth,bitmap.bmHeight,&MemDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY);
MemDC.DeleteDC();
::DeleteObject(&bitmap);
/*************自定义函数************把内存数据转为HBITMAP***************************/
HBITMAP CDBPicDllDlg::MemTOPic(char *buff)
{
HBITMAP hBitmap=NULL;
LPSTR hDib=NULL;
LPSTR PicBuff=NULL;
PicBuff=buff;
LPVOID pDibBit=NULL;
BITMAPFILEHEADER bmpHead;//位图头结构
DWORD LenBmpHead;
LenBmpHead=sizeof(BITMAPFILEHEADER);
strncpy((LPSTR)&bmpHead,PicBuff,LenBmpHead);
// memcpy((LPSTR)&bmpHead,PicBuff,LenBmpHead);
if(bmpHead.bfType!=(*(WORD*)"BM"))//(*(WORD*)"BM")
{
MessageBox("您选择的图片不是bmp图片!");
return NULL;
}
hDib=PicBuff+LenBmpHead;//hDib=PicBuff
BITMAPINFOHEADER &BitmapInfoHead=*(LPBITMAPINFOHEADER)hDib;
BITMAPINFO &BitmapInfo=*(LPBITMAPINFO)hDib;
pDibBit=PicBuff+((BITMAPFILEHEADER*)PicBuff)->bfOffBits;
CClientDC dc(this);
hBitmap=CreateDIBitmap(dc.m_hDC,&BitmapInfoHead,CBM_INIT,pDibBit,&BitmapInfo,DIB_RGB_COLORS);
return hBitmap;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
在SQL2000下方法也是相同的,这里就不重复了。由于时间原因今天这里只是和大家讨论了BMP文件,接下来我会把一些关于JPG的文件的在数据库的读写方法总结出来,供大家交流之用。
JPG
接下来,我们进入对JPG图片文件的读取.在开始之前,我们先来说明一下几个”小东西”,以备在接下来的时候引用.
1>
GlobalAlloc()根据MSDN中的解释它就是在堆上分配指定大小的空间.,同时在使用它来分配内存空间的时候,一定要用GlobalLock来锁定,不然的话,它可能会被系统认为是非法的现时被回收.在使用完之后一定要记得用GlobalUnlock来解锁.说到这里大家可以再到网上去查一个与之类似的如: HeapAlloc, VirtualAlloc,new,malloc的区别,以及这们的用法和相对应的释放.(大家一定要理解在堆上分配空间与在堆栈上分配空间的区别.)
2> IStream:
The IStream interface lets you read and write data to stream objects. Stream objects contain the data in a structured storage object, where storages provide the structure. Simple data can be written directly to a stream but, most frequently, streams are elements nested within a storage object. They are similar to standard files.( (来自MSDN))
3> IPicture:
The IPicture interface manages a picture object and its properties. Picture objects provide a language-neutral abstraction for bitmaps, icons, and metafiles. As with the standard font object, the system provides a standard implementation of the picture object. Its primary interfaces are IPicture and IPictureDisp, the latter being derived from IDispatch to provide access to the picture's properties through Automation. A picture object is created with OleCreatePictureIndirect. (来自MSDN)
4>
CreateStreamOnHGlobal(…)//在全局内存空间中创建一个流对象
5>
OleLoadPicture(..)//
OleLoadPicture从流中加载图像并创建一个可用来显示图像的新IPicture对象。
提示:可能有的朋友很讨厌我上面用了MSDN中的一段原来来解释,主要原因是因为小弟我E文太差读出来的东西也只以我自己听而已,所以就不在这里献丑了,它们都是一些关于COM的接口,有兴趣的朋友可以自己去查找这方面的资料!
开始进入正方:
开始的创建数据库,连接数据库的操作今天在这里就不重复了,有不明白的可以参考一下我个人空间中的文章看看.
/***********这一段就不用解释了,是用来获得文件的路径************/
CString strFindPic="JPG Files (*.jpg)|*.jpg|All Files (*.*)|*.*||";
CFileDialog FindPic(TRUE,"",NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,strFindPic);
CString FilePath("");
if(FindPic.DoModal()==IDOK)
{
FilePath=FindPic.GetPathName();
// SetDlgItemText(IDC_EDIT1,FilePath);
if(FilePath.IsEmpty())
{
MessageBox("载入图片失败!");
return;
}
}
else
{
return;
}
/********End***这一段就不用解释了,是用来获得文件的路径************/
/***************读取文件*******************/
//为了把文件读取出来,我们定义以下几个变量
CFile file;//文件对象
BYTE *bPic;//存放文件(图片)数据
DWORD lenFile;//文件大小
if(!file.Open(FilePath,CFile::modeRead|CFile::typeBinary))//打开文件,并判断是否成功
{
MessageBox("Open file is falied!");
return;
}
lenFile=file.GetLength();//得到文件长度
bPic=new BYTE[lenFile];//根据文件的长度来申请内存空间
if(!file.Read(bPic,lenFile))//把文件中的数据读取出来,放到刚才的内存空间中.判断读取是否成功
{
MessageBox("Read file is falied!");
return;
}
//下面的操作就不用说了,是和BITMAP文件的写入相同的.
/****************把数据写入数据库*****************************/
BYTE *pByte=NULL;
VARIANT varBLOB;
SAFEARRAY *psa;
SAFEARRAYBOUND rgsabound[1];
pRecord->AddNew();
try
{
if(bPic)
{
rgsabound[0].lLbound=0;
rgsabound[0].cElements=lenFile;
psa=SafeArrayCreate(VT_UI1,1,rgsabound);
for(long i=0;i<(long)lenFile;i++)
{
SafeArrayPutElement (psa, &i, bPic++);
// SafeArrayPutElement(psa,&i,bPic++);
}
varBLOB.vt=VT_ARRAY|VT_UI1;
varBLOB.parray=psa;
pRecord->PutCollect("Name",_variant_t("Cat"));
pRecord->GetFields()->GetItem("Picture")->AppendChunk(varBLOB);
pRecord->Update();
}
}
catch(_com_error e)
{
MessageBox("添加记录失败");
return ;
}
/************End****把数据写入数据库*****************************/
/*******************装载JPEG图片******************/
HGLOBAL hGlobal=GlobalAlloc(GMEM_MOVEABLE,lenFile);//分配一个图片大小的内存,内存的形式可以根据第一个参数的不同来申请到的形式也是不一样的
if(!hGlobal)//判断申请是否成功
{
MessageBox("Can not allcate enough memory!");
return;
}
void* pData=GlobalLock(hGlobal);//锁定
memcpy(pData,buff,lenFile);//内存拷贝
GlobalUnlock(hGlobal);//解锁
IStream* pStream=NULL;//定义一个流对象
IPicture* m_IPicture;//定义一个IPicture对象
if(CreateStreamOnHGlobal(hGlobal,TRUE,&pStream)==S_OK)//在全局内存空间中创建一个流对象,并根据返回值来判断创建是否成功
{
HRESULT hr; if((hr=OleLoadPicture(pStream,lenFile,FALSE,IID_IPicture,(LPVOID*)&m_IPicture))==E_NOINTERFACE)//
从流中加载图像并创建一个可用来显示图像的新IPicture对象
{
MessageBox("LoadPicture is falied!");
return;
}
else
{
pStream->Release();
pStream=NULL;
}
}
FreeResource(hGlobal);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//些段注释掉的代码用来把jpg转成了位图来显示出来,方法我我在BITMAP中写到的判断用PICTURE控件的方法相同
/* CClientDC *pDC=new CClientDC(this);
CDC memDC;
HBITMAP hBitamp;
HANDLE handle;
m_IPicture->get_Handle((OLE_HANDLE*)&handle);
hBitamp=(HBITMAP)CopyImage(handle,IMAGE_BITMAP,0,0,LR_COPYRETURNORG);
CBitmap cBitmap;
BITMAP bitmap;
cBitmap.Attach(hBitamp);
cBitmap.GetBitmap(&bitmap);
m_Pic.SetBitmap(hBitamp);//显示在一个PICTURE控件上
memDC.CreateCompatibleDC(pDC);//1
CBitmap* oldBitamp=memDC.SelectObject(&cBitmap);//2一定要注意这句的顺序
int a=bitmap.bmWidth;
int b=bitmap.bmHeight;
if(!pDC->StretchBlt(0,0,a,b,&memDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY))
{
MessageBox("把JPG图片作为位图显示出错!");
}
memDC.SelectObject(oldBitmap);*/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
下面是有关 IPicture 的方法描述://些图出处www.vckbase.com
方法
描述
get_Handle
返回图像对象的Windows GDI句柄
get_Hpal
返回图像对象当前使用的调 {MOD}板拷贝
get_Type
返回当前图像对象的的图像类型
get_Width
返回当前图像对象的图像宽度
get_Height
返回当前图像对象的图像高度
Render
在指定的位置、指定的设备上下文上绘制指定的图像部分
set_Hpal
设置当前图像的调 {MOD}板
get_CurDC
返回当前选中这个图像的设备上下文
SelectPicture
将一个位图图像选入给定的设备上下文,返回选中图像的设备上下文和图像的GDI句柄
get_KeepOriginalForma
返回图像对象KeepOriginalFormat 属性的当前值
put_KeepOriginalFormat
设置图像对象的KeepOriginalFormat 属性
PictureChanged
通知图像对象它的图像资源改变了
SaveAsFile
将图像数据存储到流中,格式与存成文件格式相同
get_Attributes
返回图像位属性当前的设置
/* m_IPicture->get_Height(&m_Height);
m_IPicture->get_Width(&m_Width);
*/
把图片显示出来:
long Width = 0;
long Height = 0;
int MagnifyX=0;
int MagnifyY=0;
m_IPicture->get_Width(&Width);
m_IPicture->get_Height(&Height);
if(MagnifyX == NULL) MagnifyX = 0;
if(MagnifyY == NULL) MagnifyY = 0;
MagnifyX = int(MulDiv(Width, pDC->GetDeviceCaps(LOGPIXELSX), HIMETRIC_INCH) * MagnifyX);
MagnifyY = int(MulDiv(Height,pDC->GetDeviceCaps(LOGPIXELSY), HIMETRIC_INCH) * MagnifyY);
CRect DrawRect(100,100, MagnifyX, MagnifyY);
HRESULT hrP = NULL;
hrP = m_IPicture->Render(pDC->m_hDC,
100, // Left
100, // Top
bitmap.bmWidth +MagnifyX, // Width
bitmap.bmHeight +MagnifyY, // Height
0,
Height,
Width,
-Height,
&DrawRect);ct);//关于Render的使用,大家可以查一下MSDN里面讲得很清楚.