如何在运行时将多个图像从数据库保存到 TMemoryStream 并在以后提取它们
How to save multiple images from database to TMemoryStream at runtime and extract them later on
我正在 delphi(RAD Studio) 创建一个项目。数据库 table 中的某些行存储有图像。我想在运行时提取图像(为此我使用了 TMemoryStream 数组)并在 frxReport 上显示它们。
我的代码片段如下
Public 变量 TStream 声明为
Stream2 : Array of TStream; i,k: integer
视图按钮点击事件的代码段,放在 MainForm 上,期望显示 frxReport。
`
procedure TFrmMain.btnViewClick(Sender: TObject);
begin
i := 0;
k := 0;
UniTable1.SQL.Text := 'Select * from userplays order by id';
UniTable1.Execute;
rowcount := UniTable1.RecordCount;
SetLength(myid, rowcount);
SetLength(mydesc, rowcount);
SetLength(myimg, rowcount);
SetLength(Stream2, rowcount);
while not UniTable1.Eof do
begin
try
Stream2[k] := TMemoryStream.Create;
myid[k] := UniTable1.FieldByName('id').Value;
Stream2[k] := UniTable1.CreateBlobStream(TBlobField(UniTable1.FieldByName('image')), bmRead);
mydesc[k] := UniTable1.FieldByName('description').Value;
UniTable1.Next;
inc(k);
finally
//Stream2[k].Free;
end;
end;
frxUserDataSet1.RangeEnd := reCount;
frxUserDataSet1.RangeEndCount := rowcount;
frxReport1.ShowReport;
i := 0;
end;
`
但是,此方法未将任何图像加载到 Stream2 数组。有一个选项可以使用 JPEGImage 数组,但是如果使用 JPEGImage 数组,那么在
的 frxRaport 上显示它就会有问题
procedure TFrmMain.frxReport1GetValue(const VarName: string; var Value: Variant);
`
Graphic := TJPEGImage.Create;
Graphic.LoadFromStream(Stream2[j]);
TfrxPictureView(frxreport1.FindObject('Picture1')).Picture.Graphic := Graphic;
`
请告诉我如何执行此操作。
However this method is not loading any image to Stream2 array
在您当前的代码中,您首先将新创建的 TMemoryStream
分配给 Stream2[k]
:
Stream2[k] := TMemoryStream.Create;
然后你将 TMemoryStream
扔掉(造成内存泄漏)并将其替换为你创建的 blob 流:
Stream2[k] := UniTable1.CreateBlobStream(TBlobField(UniTable1.FieldByName('image')), bmRead);
但您从未从 blob 流中读取过。
这是重写的 while
循环(未经测试)
var
blobstream: TStream;
Stream2: array of TMemoryStream;
....
// read 'id', 'description' and 'image' fields to respective arrays
while not UniTable1.Eof do
begin
myid[k] := UniTable1.FieldByName('id').Value;
mydesc[k] := UniTable1.FieldByName('description').Value;
blobstream := UniTable1.CreateBlobStream(TBlobField(UniTable1.FieldByName('image')), bmRead);
try
Stream2[k] := TMemoryStream.Create;
Stream2[k].LoadFromStream(blobstream);
finally
blobstream.Free;
end;
UniTable1.Next;
inc(k);
end;
顺便说一句,我建议定义一个记录来保存 id
、description
和 image
,然后定义一个包含这些记录的数组,而不是三个单独的数组。只管理一个阵列而不是三个阵列要简单得多。
我认为在您的情况下,最明显的事情就是将现有的 DataSet (Unitable1) 直接连接到 frxReport 中的 DataBand。我不明白为什么您需要使用 frxUserDataset 而不是 frxDBDataset 并将其 DataSet 属性 设置为 Unitable1,因为无论如何您都创建了它。
如果您确实需要一个对象列表,那么我会采用不同的方法。
你的多个全局变量看起来很难看而且很危险 (i,k...)
我会创建一个 class 来保存您的数据并将这些数据填充到
来自 System.Generics.Collection 的 TObjectList:
unit DataPack;
interface
uses ExtCtrls...System.SysUtils;
type
TMyDataPack = class(TObject)
private
Query: TFDQuery; //O r TUniQery
_MyId: Integer;
_MyDescription: String;
_MyImage: TImage;
_Loaded: Boolean;
function LoadData: boolean;
protected
public
constructor Create(Data: TFDQuery); //Or Uniquery
property MyId: Integer read _MyId;
property MyDescription: String read _MyDescription;
property MyImage: TImage read _MyImage;
property Loaded: Boolean read _Loaded;
end;
implementation
{ TMyDataPack }
constructor TMyDataPack.Create(Data: TFDQuery);
begin
Inherited Create;
Query:=Data;
_Loaded:=true;
if not (LoadData) then
_Loaded:=false;
end;
function TMyDataPack.LoadData: boolean;
var
Stream: TStream;
begin
Stream:= TStream.Create;
Stream:=Query.CreateBlobStream(TBlobField(Query.FieldByName('image')), bmRead);
try
_MyImage:=TImage.Create(nil);
_MyImage.Picture.Bitmap.LoadFromStream(Stream);
_MyDescription:=Query.FieldByName('description').AsString;
_MyId:=Query.FieldByName('CategoryId').AsInteger;
except on E: Exception do
begin
FreeAndNil(Stream);
FreeAndNil(_MyImage);
Exit(false);
end;
end;
FreeAndNil(Stream);
Result:=true;
end;
end.
然后在你的主代码中:
procedure TMain.Button2Click(Sender: TObject);
var
DataRow: TMyDataPack;
List: TObjectList<TMyDataPack>;
Num: integer;
CurrentImage: TImage;
CurrentDescription: String;
CurrentId: integer;
begin
UniTable1.SQL.Text:='SELECT * FROM Userplays';
UniTable1.Open();
List:=TObjectList<TMyDataPack>.Create;
while not (UniTable1.Eof) do
begin
DataRow:=TMyDataPack.Create(UniTable1);
if(DataRow.Loaded) then
List.Add(DataRow);
UniTable1.Next;
End;
for Num:=0 to List.Count do
begin
CurrentDescription:=List[Num].MyDescription;
CurrentImage:=List[Num].MyImage;
CurrentId:=List[Num].MyId;
//List[Num].MyImage.Picture.SaveToFile('Some'+IntToStr(Num)+'.bmp');
// You might save it to file then...
end;
end;
end.
然后再传递一个ObjectList,最后记得释放它。
您可以通过创建 class 工厂来进一步扩展它,以创建不同的对象,例如,如果您需要使用具有相同数据的不同数据库,
执行检查图像是否实际上是 Bmp 或 Jpg 等等。
您甚至可以只传递一个查询而不将其设置为 SQL 并将 TobjectList 设置为 属性.
我正在 delphi(RAD Studio) 创建一个项目。数据库 table 中的某些行存储有图像。我想在运行时提取图像(为此我使用了 TMemoryStream 数组)并在 frxReport 上显示它们。
我的代码片段如下
Public 变量 TStream 声明为
Stream2 : Array of TStream; i,k: integer
视图按钮点击事件的代码段,放在 MainForm 上,期望显示 frxReport。
`
procedure TFrmMain.btnViewClick(Sender: TObject);
begin
i := 0;
k := 0;
UniTable1.SQL.Text := 'Select * from userplays order by id';
UniTable1.Execute;
rowcount := UniTable1.RecordCount;
SetLength(myid, rowcount);
SetLength(mydesc, rowcount);
SetLength(myimg, rowcount);
SetLength(Stream2, rowcount);
while not UniTable1.Eof do
begin
try
Stream2[k] := TMemoryStream.Create;
myid[k] := UniTable1.FieldByName('id').Value;
Stream2[k] := UniTable1.CreateBlobStream(TBlobField(UniTable1.FieldByName('image')), bmRead);
mydesc[k] := UniTable1.FieldByName('description').Value;
UniTable1.Next;
inc(k);
finally
//Stream2[k].Free;
end;
end;
frxUserDataSet1.RangeEnd := reCount;
frxUserDataSet1.RangeEndCount := rowcount;
frxReport1.ShowReport;
i := 0;
end;
`
但是,此方法未将任何图像加载到 Stream2 数组。有一个选项可以使用 JPEGImage 数组,但是如果使用 JPEGImage 数组,那么在
的 frxRaport 上显示它就会有问题
procedure TFrmMain.frxReport1GetValue(const VarName: string; var Value: Variant);
`
Graphic := TJPEGImage.Create;
Graphic.LoadFromStream(Stream2[j]);
TfrxPictureView(frxreport1.FindObject('Picture1')).Picture.Graphic := Graphic;
` 请告诉我如何执行此操作。
However this method is not loading any image to Stream2 array
在您当前的代码中,您首先将新创建的 TMemoryStream
分配给 Stream2[k]
:
Stream2[k] := TMemoryStream.Create;
然后你将 TMemoryStream
扔掉(造成内存泄漏)并将其替换为你创建的 blob 流:
Stream2[k] := UniTable1.CreateBlobStream(TBlobField(UniTable1.FieldByName('image')), bmRead);
但您从未从 blob 流中读取过。
这是重写的 while
循环(未经测试)
var
blobstream: TStream;
Stream2: array of TMemoryStream;
....
// read 'id', 'description' and 'image' fields to respective arrays
while not UniTable1.Eof do
begin
myid[k] := UniTable1.FieldByName('id').Value;
mydesc[k] := UniTable1.FieldByName('description').Value;
blobstream := UniTable1.CreateBlobStream(TBlobField(UniTable1.FieldByName('image')), bmRead);
try
Stream2[k] := TMemoryStream.Create;
Stream2[k].LoadFromStream(blobstream);
finally
blobstream.Free;
end;
UniTable1.Next;
inc(k);
end;
顺便说一句,我建议定义一个记录来保存 id
、description
和 image
,然后定义一个包含这些记录的数组,而不是三个单独的数组。只管理一个阵列而不是三个阵列要简单得多。
我认为在您的情况下,最明显的事情就是将现有的 DataSet (Unitable1) 直接连接到 frxReport 中的 DataBand。我不明白为什么您需要使用 frxUserDataset 而不是 frxDBDataset 并将其 DataSet 属性 设置为 Unitable1,因为无论如何您都创建了它。
如果您确实需要一个对象列表,那么我会采用不同的方法。 你的多个全局变量看起来很难看而且很危险 (i,k...)
我会创建一个 class 来保存您的数据并将这些数据填充到 来自 System.Generics.Collection 的 TObjectList:
unit DataPack;
interface
uses ExtCtrls...System.SysUtils;
type
TMyDataPack = class(TObject)
private
Query: TFDQuery; //O r TUniQery
_MyId: Integer;
_MyDescription: String;
_MyImage: TImage;
_Loaded: Boolean;
function LoadData: boolean;
protected
public
constructor Create(Data: TFDQuery); //Or Uniquery
property MyId: Integer read _MyId;
property MyDescription: String read _MyDescription;
property MyImage: TImage read _MyImage;
property Loaded: Boolean read _Loaded;
end;
implementation
{ TMyDataPack }
constructor TMyDataPack.Create(Data: TFDQuery);
begin
Inherited Create;
Query:=Data;
_Loaded:=true;
if not (LoadData) then
_Loaded:=false;
end;
function TMyDataPack.LoadData: boolean;
var
Stream: TStream;
begin
Stream:= TStream.Create;
Stream:=Query.CreateBlobStream(TBlobField(Query.FieldByName('image')), bmRead);
try
_MyImage:=TImage.Create(nil);
_MyImage.Picture.Bitmap.LoadFromStream(Stream);
_MyDescription:=Query.FieldByName('description').AsString;
_MyId:=Query.FieldByName('CategoryId').AsInteger;
except on E: Exception do
begin
FreeAndNil(Stream);
FreeAndNil(_MyImage);
Exit(false);
end;
end;
FreeAndNil(Stream);
Result:=true;
end;
end.
然后在你的主代码中:
procedure TMain.Button2Click(Sender: TObject);
var
DataRow: TMyDataPack;
List: TObjectList<TMyDataPack>;
Num: integer;
CurrentImage: TImage;
CurrentDescription: String;
CurrentId: integer;
begin
UniTable1.SQL.Text:='SELECT * FROM Userplays';
UniTable1.Open();
List:=TObjectList<TMyDataPack>.Create;
while not (UniTable1.Eof) do
begin
DataRow:=TMyDataPack.Create(UniTable1);
if(DataRow.Loaded) then
List.Add(DataRow);
UniTable1.Next;
End;
for Num:=0 to List.Count do
begin
CurrentDescription:=List[Num].MyDescription;
CurrentImage:=List[Num].MyImage;
CurrentId:=List[Num].MyId;
//List[Num].MyImage.Picture.SaveToFile('Some'+IntToStr(Num)+'.bmp');
// You might save it to file then...
end;
end;
end.
然后再传递一个ObjectList,最后记得释放它。
您可以通过创建 class 工厂来进一步扩展它,以创建不同的对象,例如,如果您需要使用具有相同数据的不同数据库, 执行检查图像是否实际上是 Bmp 或 Jpg 等等。 您甚至可以只传递一个查询而不将其设置为 SQL 并将 TobjectList 设置为 属性.