如何存入和显示access库中图像字段?(100分)

  • 主题发起人 主题发起人 小女人
  • 开始时间 开始时间

小女人

Unregistered / Unconfirmed
GUEST, unregistred user!
如何存入和显示access库中图像字段?
 
//装入:
Dataset.edit; //or insert,append
TBlobFiled(Dataset.fieldByName('blobFiled')).LoadFromFile(FileName);
//从文件中装入,也可以从流中装入LoadFromStream(流变量),流变量通常是TMemoryStream类型。
Dataset.Post;

//显示
TBlobField(Dataset.FieldByName('BlobField')).SaveToFile(TempFileName);
//保存到临时文件,也可保存到流变量
Image1.Picture.LoadFromFile(TempFileName);
//从临时文件加载图片,也可从流变量
deleteFile(TempFileName); //删除临时文件。

 
哪里有相关的更详细资料?
 
那你搜索一下吧,有很多相关内容的。另外关于流的操作,可以给你转贴一个帖子:

谈Delphi编程中“流”的利用

什么是流?流,简单来说就是建立在面向对象基础上的一种抽象的处理数据的工具。在流中,定义了一些处理数据的基本操作,如读取数据,写入数据等,程序员是对流进行所有操作的,而不用关心流的另一头数据的真正流向。流不但可以处理文件,还可以处理动态内存、网络数据等多种数据形式。如果你对流的操作非常熟练,在程序中利用流的方便性,写起程序会大大提高效率的。
下面,笔者通过四个实例:EXE文件加密器、电子贺卡、自制OICQ和网络屏幕传输来说明Delphi编程中“流”的利用。这些例子中的一些技巧曾经是很多软件的秘密而不公开的,现在大家可以无偿的直接引用其中的代码了。
“万丈高楼平地起”,在分析实例之前,我们先来了解一下流的基本概念和函数,只有在理解了这些基本的东西后我们才能进行下一步。请务必认真领会这些基本方法。当然,如果你对它们已经很熟悉了,则可以跳过这一步。

一、Delphi中流的基本概念及函数声明
在Delphi中,所有流对象的基类为TStream类,其中定义了所有流的共同属性和方法。
TStream类中定义的属性介绍如下:
1、Size:此属性以字节返回流中数据大小。
2、Position:此属性控制流中存取指针的位置。
Tstream中定义的虚方法有四个:
1、Read:此方法实现将数据从流中读出。函数原形为:
Function Read(var Buffer;Count:Longint):Longint;virtual;abstract;
参数Buffer为数据读出时放置的缓冲区,Count为需要读出的数据的字节数,该方法返回值为实际读出的字节数,它可以小于或等于Count中指定的值。
2、Write:此方法实现将数据写入流中。函数原形为:
Function Write(var Buffer;Count:Longint):Longint;virtual;abstract;
参数Buffer为将要写入流中的数据的缓冲区,Count为数据的长度字节数,该方法返回值为实际写入流中的字节数。
3、Seek:此方法实现流中读取指针的移动。函数原形为:
Function Seek(Offset:Longint;Origint:Word):Longint;virtual;abstract;
参数Offset为偏移字节数,参数Origint指出Offset的实际意义,其可能的取值如下:
soFromBeginning:Offset为移动后指针距离数据开始的位置。此时Offset必须大于或者等于零。
soFromCurrent:Offset为移动后指针与当前指针的相对位置。
soFromEnd:Offset为移动后指针距离数据结束的位置。此时Offset必须小于或者等于零。
该方法返回值为移动后指针的位置。
4、Setsize:此方法实现改变数据的大小。函数原形为:
Function Setsize(NewSize:Longint);virtual;
另外,TStream类中还定义了几个静态方法:
1、ReadBuffer:此方法的作用是从流中当前位置读取数据。函数原形为:
Procedure ReadBuffer(var Buffer;Count:Longint);
参数的定义跟上面的Read相同。注意:当读取的数据字节数与需要读取的字节数不相同时,将产生EReadError异常。
2、WriteBuffer:此方法的作用是在当前位置向流写入数据。函数原形为:
Procedure WriteBuffer(var Buffer;Count:Longint);
参数的定义跟上面的Write相同。注意:当写入的数据字节数与需要写入的字节数不相同时,将产生EWriteError异常。
3、CopyFrom:此方法的作用是从其它流中拷贝数据流。函数原形为:
Function CopyFrom(Source:TStream;Count:Longint):Longint;
参数Source为提供数据的流,Count为拷贝的数据字节数。当Count大于0时,CopyFrom从Source参数的当前位置拷贝Count个字节的数据;当Count等于0时,CopyFrom设置Source参数的Position属性为0,然后拷贝Source的所有数据;
TStream还有其它派生类,其中最常用的是TFileStream类。使用TFileStream类来存取文件,首先要建立一个实例。声明如下:
constructor Create(const Filename:string;Mode:Word);
Filename为文件名(包括路径),参数Mode为打开文件的方式,它包括文件的打开模式和共享模式,其可能的取值和意义如下:

打开模式:
fmCreate :用指定的文件名建立文件,如果文件已经存在则打开它。
fmOpenRead :以只读方式打开指定文件
fmOpenWrite :以只写方式打开指定文件
fmOpenReadWrite:以写写方式打开指定文件
共享模式:
fmShareCompat :共享模式与FCBs兼容
fmShareExclusive:不允许别的程序以任何方式打开该文件
fmShareDenyWrite:不允许别的程序以写方式打开该文件
fmShareDenyRead :不允许别的程序以读方式打开该文件
fmShareDenyNone :别的程序可以以任何方式打开该文件

TStream还有一个派生类TMemoryStream,实际应用中用的次数也非常频繁。它叫内存流,就是说在内存中建立一个流对象。它的基本方法和函数跟上面是一样的。
好了,有了上面的基础后,我们就可以开始我们的编程之行了。
-----------------------------------------------------------------------
二、实际应用之一:利用流制作EXE文件加密器、捆绑、自解压文件及安装程序

我们先来说一下如何制作一个EXE文件加密器吧。
EXE文件加密器的原理:建立两个文件,一个用来添加资源到另外一个EXE文件里面,称为添加程序。另外一个被添加的EXE文件称为头文件。该程序的功能是把添加到自己里面的文件读出来。Windows下的EXE文件结构比较复杂,有的程序还有校验和,当发现自己被改变后会认为自己被病毒感染而拒绝执行。所以我们把文件添加到自己的程序里面,这样就不会改变原来的文件结构了。我们先写一个添加函数,该函数的功能是把一个文件当作一个流添加到另外一个文件的尾部。函数如下:

Function Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
Target,Source:TFileStream;
MyFileSize:integer;
begin
try
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite or fmShareExclusive);
try
Target.Seek(0,soFromEnd);//往尾部添加资源
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);//计算资源大小,并写入辅程尾部
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
finally
Target.Free;
Source.Free;
end;
except
Result:=False;
Exit;
end;
Result:=True;
end;
有了上面的基础,我们应该很容易看得懂这个函数。其中参数SourceFile是要添加的文件,参数TargetFile是被添加到的目标文件。比如说把a.exe添加到b.exe里面可以:Cjt_AddtoFile('a.exe',b.exe');如果添加成功就返回True否则返回假。
根据上面的函数我们可以写出相反的读出函数:
Function Cjt_LoadFromFile(SourceFile,TargetFile :string):Boolean;
var
Source:TFileStream;
Target:TMemoryStream;
MyFileSize:integer;
begin
try
Target:=TMemoryStream.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
try
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//读出资源大小
Source.Seek(-MyFileSize,soFromEnd);//定位到资源位置
Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//取出资源
Target.SaveToFile(TargetFile);//存放到文件
finally
Target.Free;
Source.Free;
end;
except
Result:=false;
Exit;
end;
Result:=true;
end;
其中参数SourceFile是已经添加了文件的文件名称,参数TargetFile是取出文件后保存的目标文件名。比如说Cjt_LoadFromFile('b.exe','a.txt');在b.exe中取出文件保存为a.txt。如果取出成功就返回True否则返回假。
打开Delphi,新建一个工程,在窗口上放上一个Edit控件Edit1和两个Button:Button1和Button2。Button的Caption属性分别设置为“确定”和“取消”。在Button1的Click事件中写代码:
var S:string;
begin
S:=ChangeFileExt(Application.ExeName,'.Cjt');
if Edit1.Text='790617' then
begin
Cjt_LoadFromFile(Application.ExeName,S);
{取出文件保存在当前路径下并命名"原文件.Cjt"}
Winexec(pchar(S),SW_Show);{运行"原文件.Cjt"}
Application.Terminate;{退出程序}
end
else
Application.MessageBox('密码不对,请重新输入!','密码错误',MB_ICONERROR+MB_OK);
编译这个程序,并把EXE文件改名为head.exe。新建一个文本文件head.rc,内容为: head exefile head.exe,然后把它们拷贝到Delphi的BIN目录下,执行Dos命令Brcc32.exe head.rc,将产生一个head.res的文件,这个文件就是我们要的资源文件,先留着。
我们的头文件已经建立了,下面我们来建立添加程序。
新建一个工程,放上以下控件:一个Edit,一个Opendialog,两个Button1的Caption属性分别设置为"选择文件"和"加密"。在源程序中添加一句:{$R head.res}并把head.res文件拷贝到程序当前目录下。这样一来就把刚才的head.exe跟程序一起编译了。
在Button1的Cilck事件里面写下代码:
if OpenDialog1.Execute then Edit1.Text:=OpenDialog1.FileName;
在Button2的Cilck事件里面写下代码:
var S:String;
begin
S:=ExtractFilePath(Edit1.Text);
if ExtractRes('exefile','head',S+'head.exe') then
if Cjt_AddtoFile(Edit1.Text,S+'head.exe') then
if DeleteFile(Edit1.Text) then
if RenameFile(S+'head.exe',Edit1.Text) then
Application.MessageBox('文件加密成功!','信息',MB_ICONINFORMATION+MB_OK)
else
begin
if FileExists(S+'head.exe') then DeleteFile(S+'head.exe');
Application.MessageBox('文件加密失败!','信息',MB_ICONINFORMATION+MB_OK)
end;
end;
其中ExtractRes为自定义函数,它的作用是把head.exe从资源文件中取出来。
Function ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res : TResourceStream;
begin
try
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
try
Res.SavetoFile(ResNewName);
Result:=true;
finally
Res.Free;
end;
except
Result:=false;
end;
end;
注意:我们上面的函数只不过是简单的把一个文件添加到另一个文件的尾部。实际应用中可以改成可以添加多个文件,只要根据实际大小和个数定义好偏移地址就可以了。比如说文件捆绑机就是把两个或者多个程序添加到一个头文件里面。那些自解压程序和安装程序的原理也是一样的,不过多了压缩而已。比如说我们可以引用一个LAH单元,把流压缩后再添加,这样文件就会变的很小。读出来时先解压就可以了。另外,文中EXE加密器的例子还有很多不完善的地方,比如说密码固定为"790617",取出EXE运行后应该等它运行完毕后删除等等,读者可以自行修改。

---------------------------------------------------------------------
三、实际应用之二:利用流制作可执行电子贺卡

我们经常看到一些电子贺卡之类的制作软件,可以让你自己选择图片,然后它会生成一个EXE可执行文件给你。打开贺卡时就会一边放音乐一边显示出图片来。现在学了流操作之后,我们也可以做一个了。
添加图片过程我们可以直接用前面的Cjt_AddtoFile,而现在要做的是如何把图像读出并显示。我们用前面的Cjt_LoadFromFile先把图片读出来保存为文件再调入也是可以的,但是还有更简单的方法,就是直接把文件流读出来显示,有了流这个利器,一切都变的简单了。
现在的图片比较流行的是BMP格式和JPG格式。我们现在就针对这两种图片写出读取并显示函数。

Function Cjt_BmpLoad(ImgBmp:TImage;SourceFile:String):Boolean;
var
Source:TFileStream;
MyFileSize:integer;
begin
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
try
try
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//读出资源
Source.Seek(-MyFileSize,soFromEnd);//定位到资源开始位置
ImgBmp.Picture.Bitmap.LoadFromStream(Source);
finally
Source.Free;
end;
except
Result:=False;
Exit;
end;
Result:=True;
end;
上面是读出BMP图片的,下面的是读出JPG图片的函数,因为要用到JPG单元,所以要在程序中添加一句:uses jpeg。

Function Cjt_JpgLoad(JpgImg:Timage;SourceFile:String):Boolean;
var
Source:TFileStream;
MyFileSize:integer;
Myjpg: TJpegImage;
begin
try
Myjpg:= TJpegImage.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
try
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));
Source.Seek(-MyFileSize,soFromEnd);
Myjpg.LoadFromStream(Source);
JpgImg.Picture.Bitmap.Assign(Myjpg);
finally
Source.Free;
Myjpg.free;
end;
except
Result:=false;
Exit;
end;
Result:=true;
end;
有了这两个函数,我们就可以制作读出程序了。下面我们以BMP图片为例:
运行Delphi,新建一个工程,放上一个显示图像控件Image1。在窗口的Create事件中写上一句就可以了:
Cjt_BmpLoad(Image1,Application.ExeName);
这个就是头文件了,然后我们用前面的方法生成一个head.res资源文件。
下面就可以开始制作我们的添加程序了。全部代码如下:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, ExtDlgs;

type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
OpenPictureDialog1: TopenPictureDialog
 
//使用了一个TDBImage部件来显示数据库表中的位图图像。
http://www.fosu.edu.cn/netschool/delphi/040.htm
 
这是我程序中的一段,希望能够给你帮助
//保存
txst:=TMemoryStream.Create;
Q_Photo.First;
while not Q_photo.Eof do //从Oracle中读取图像
begin
TBlobField(Q_Photo.FieldByName('照片')).SaveToStream(txst);
Q_Photo.Next;
end;
txst.Position:=0;
AQ_photo.Close; //保存到 Access中
AQ_photo.SQL.Text:='Insert Into PeccPhoto values (:dm,:zp)';
AQ_Photo.Parameters.ParamByName('dm').Value:=Q_Pecc.fieldbyname('代码').AsString;
AQ_Photo.Parameters.ParamByName('zp').LoadFromStream(txst,ftBlob);
try
ADOConnection1.BeginTrans;
AQ_Photo.ExecSQL;
ADOConnection1.CommitTrans;
except
ADOConnection1.RollbackTrans;
MessageDlg('照片更新失败。',mtError,[MbOk],0);
Abort;
end;
txst.free;
//读取
picStr:=TMemoryStream.Create;
TBlobField(Q_photo.FieldByName('photo')).SaveToStream(picStr);
picstr.Position:=0;
PicXs:=TJPEGImage.Create;
PicXs.LoadFromStream(PicStr);
PicStr.Free;
 
本来我很拿手的,我最近刚做了这样的程序,没想到高手很多,都说完了。
只好凑热闹了![:D]
 
以前有人回答过的

来自:caojun77

如何实现在MS Access数据库中图像的存储和显示
一、 原理介绍——流式数据的类型及其应用
在Dephi中提供了TStream来支持对流式数据的操作。TStream是万流之源,但由于它是一个抽象类,故不能被直接使用;而要使用其相应的子类,如:TFileStream 、TStringStream、TMemoryStream、TBlobStream、TWinSocketStream和TOleStream。TStream提供了统一、简洁的方法来进行数据的读写。
1.)SaveToStream(Stream: TStream ); 作用:将类中的数据写到Stream的当前位置中
2.)LoadFromStream(Stream: TStream); 作用:从当前位置读入Stream里的数据
实际使用时我们基本上只要使用上面两个函数就可以了。
二、所遇到的问题及相应的解决方法
为了节省图像的存储空间和使用更加方便,决定采用JPEG这种图像格式。
(一)所遇到的问题
第一、在Delphi 5中进行画图所用到的组件是TImage,所生成的图像的格式为BMP格式,而为了节省图像的存储空间,图像在数据库里存储的格式须为JPEG格式,这样就产生了图像格式转化的需求;而TImage本身并不直接提供这两种图像格式之间的转化。
第二、怎样将存储在Microsoft Access数据库中的图像取出并且显示出来:在Delphi 5中,能提供这种功能的组件是TDBImage,但该组件却存在着一个很大的缺陷:它所能显示的图像类型只能是一些图标文件,元文件和BMP文件,而不能支持JPEG格式的图像在该组件中的显示;但根据实际需要,在Microsoft Access数据库中所存储的图像数据却是以JPEG格式保存的。
(二)相应的解决方法
为了解决上述两个问题,可以采用目前数据库中一种名为大二分对象(BLOB——Binary Large Object),它是用来处理某些特殊格式的数据的。BLOB在数据库的表中实际上是以二进制数据的形式存放的。
为了处理BLOB字段,可以借鉴一些可视的桌面数据库的方法。在这里,我们选择了通过内存流的方式来完成;使用内存流,可减少磁盘操作,大大提高运行效率。
具体的过程和相关的程序代码如下:
1、如何实现在Microsoft Access数据库中的图像存储:
这里是利用TStream的子类TMemoryStream向Microsoft Access数据库中存储图像的。下面的这段代码是在按了“保存”按钮之后所触发的事件处理程序:
procedure TForm1.Button1Click(Sender: TObject);
var
MyJPEG : TJPEGImage;
MS: TMemoryStream;
begin
MyJPEG := TJPEGImage.Create;
try
with MyJPEG do
begin
Assign(Image.Picture.Graphic);
MS:=TMemoryStream.create;
SaveToStream(MS);
MS.Position:=0;
Table1.Edit;
TBlobField(Table1.FieldbyName('Image')).LoadFromStream(MS);
Table1.Post;
messagebox(getactivewindow(),'图像保存完毕!','保存',mb_ok);
end;
finally
MyJPEG.Free;
end;
end;
在这段代码里TStream的子类TMemoryStream利用内存流起到了将BMP格式转化为JPEG格式的中间桥梁的作用。
2、如何将图像从Microsoft Access数据库中取出并显示出来:
下面的这段代码是在按了“查看图像”按钮之后所触发的事件处理程序:
procedure TForm1.Button1Click(Sender: TObject);
var tempstream:TStringStream;
tempjpeg:TJPEGImage;
begin
try
tempstream:=TStringStream.Create(' ');
TBlobField(Query1.FieldByName('Image')).SaveToStream(tempstream);
tempstream.Position:=0;
tempjpeg:=TJPEGImage.Create;
tempjpeg.LoadFromStream(tempstream);
DBImage1.Picture.Bitmap.Assign(tempjpeg);
finally
tempstream.Free;
tempjpeg.Free;
end;
end;
这段代码的主要作用是:首先将查询结果中的JPEG图像格式数据保存到TStringStream中去,然后设置数据指针在TStringStream中的位置为0;接着从TStringStream中读入相关数据,并把它们赋给TDBImage.Picture.Bitmap,这样一来就实现了将数据库中所存储的JPEG格式的数据转化为BMP格式,并在TDBImage中将图像显示出来。最后将TStringStream和TJPEGImage这两个对象释放掉。特别要注意的是不能在设计阶段设置TDBImage的DataField属性,而只能通过写代码的形式在运行阶段把利用流式数据所转化过来的新格式的图像数据赋给TDBImage.Picture.Bitmap。



 
我试了caojun77的方法,可TJPEGImage错! TJPEGImage是什么类型呀?
 
要添加jpeg单元。
 
如何添加要添加jpeg单元?
 
unit FMU_M020201;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs
,JPEG;

type
TFMW_FWWDReg = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;

var
 
来自:caojun77写的很清楚了,下面是我做的存图片,(用的就是这个方法:)
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, StdCtrls, ExtCtrls, DBCtrls, Grids, DBGrids, ADODB,JPEG;

type
TForm1 = class(TForm)
ADOTable1: TADOTable;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
Image1: TImage;
Button1: TButton;
Button2: TButton;
DataSource1: TDataSource;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
opendialog1.Execute;
image1.Picture.LoadFromFile(opendialog1.filename);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
MyJPEG : TJPEGImage;
MS: TMemoryStream;
begin
MyJPEG := TJPEGImage.Create;
try
with MyJPEG do
begin
Assign(Image1.Picture.Graphic);
MS:=TMemoryStream.create;
SaveToStream(MS);
MS.Position:=0;
ADOTable1.Edit;

TBlobField(ADOTable1.FieldbyName('tu')).LoadFromStream(MS);
ADOTable1.Post;

end;
finally
MyJPEG.Free;
end;
end;

end.
 
谢谢大家了 我做出来了
 
多人接受答案了。
 
我是按照上面的作的,但怎么提示我‘invalid class typecast'错误呢,谢谢指点!
 
后退
顶部