如何在运行时将多个图像从数据库保存到 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;

顺便说一句,我建议定义一个记录来保存 iddescriptionimage,然后定义一个包含这些记录的数组,而不是三个单独的数组。只管理一个阵列而不是三个阵列要简单得多。

我认为在您的情况下,最明显的事情就是将现有的 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 设置为 属性.