如何在运行时将多个映像从数据库保存到TMemoryStream,并在以后提取它们

时间:2016-07-16 10:19:59

标签: arrays delphi blob memorystream delphi-xe3

我正在delphi(RAD Studio)创建一个项目。在某些行的数据库表中存储了图像。我想在运行时提取图像(我使用的是TMemoryStream数组)并在frxReport中显示它们。

我的代码段如下

公共变量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;

`    请告诉我如何做到这一点。

2 个答案:

答案 0 :(得分:3)

  

但是,此方法未将任何图像加载到Stream2数组

在您当前的代码中,您首先将新创建的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放在一起,然后是一组记录,而不是三个独立的数组。更简单的是只管理一个数组而不是三个。

答案 1 :(得分:2)

我认为在你的情况下最明显的事情就是将现有的DataSet(Unitable1)直接连接到frxReport中的DataBand。我不明白为什么你需要使用frxUserDataset而不是frxDBDataset,因为你的DataSet属性设置为Unitable1,无论如何都是这样。

如果你真的需要一个对象列表,那么我会采取不同的方法。 你的多个全局变量看起来很丑陋并且很危险(i,k ......)

我会创建一个包含数据的类,并将这些数据填充到一个 来自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,记得最后释放它。

如果您需要使用具有相同数据的不同数据库,可以通过创建类工厂来创建不同的对象来进一步扩展它, 执行检查图像是否实际是Bmp或Jpg等。 您甚至可以在不设置SQL的情况下传递查询,并将TobjectList作为属性。