在 Delphi 10.4 中流式传输的阵列

Array to Stream in Delphi 10.4

如何将“Array[0..9] of TJpegImage”保存并加载到 MemoryStream。 有很多示例将一个图像保存到流中,但没有一个保存数组。 我需要它将 Stream 保存到数据库 Blob 字段,然后将 BlobField 加载回我的数组。 数组中的所有图像具有相同的大小。 也许 TByte 流可以做到这一点? 但是如何将 TByte-Stream 转换回数组?

更新: 非常感谢 MBo 的示例。 我尝试使用它并遇到了一些问题。 我使用 Paintbox 绘制图片并在地址 0x000 的 0x0062b9ab:read 处发生访问冲突....

这是我尝试的代码:

单位PicToMemStream; 界面

使用 Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls、Vcl.Forms、Vcl.Dialogs、Vcl.Imaging.jpeg、Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    PaintBox: TPaintBox;
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


var
  ms, temp: TMemoryStream;
  jps: array of TJpegImage;
  i, n, sz: Integer;
  ProgPath: String;
begin
  ProgPath := ExtractFilePath(Application.ExeName);
  n := 3;
  SetLength(jps, n);
  ms := TMemoryStream.Create;
  temp := TMemoryStream.Create;

  for i := 0 to n - 1 do
  begin
    jps[i] := TJpegImage.Create;
    jps[i].LoadFromFile(Format(ProgPath + '%s.jpg', [Chr(Ord('a') + i)]));
    // in my case loads files a.jpg, b.jpg, c.jpg
  end;

  // Save Pictures to Stream and later in BlobField (100kByte)
  try
    ms.Write(n, SizeOf(n));
    for i := 0 to n - 1 do
    begin

      temp.Clear;
      // Save Image Data first here
      jps[i].SaveToStream(temp); // jpeg data here

      sz := temp.Size;
      // Save length second here
      ms.Write(sz, SizeOf(sz)); // size of image

      temp.Position := 0;
      ms.CopyFrom(temp, sz); // image data to final destination
    end;

    ms.SaveToFile(ProgPath + 'base.dat'); // for control // later here Write Data to BLob


    // Read Picture and show it in a Paintbox or Image
    // revert to empty stream
    ms.Clear;
    ms.LoadFromFile(ProgPath + 'base.dat'); // load from "base" // later here Read Data from Blob

    ms.Read(n, SizeOf(n));
    // here some actions to setup array

    for i := 0 to n - 1 do // Read 3 times a picture
    begin
      ms.Read(sz, SizeOf(sz)); // Read Lenth of Picturedata first

      temp.Clear; // Read Picturedata in jps[i]
      temp.CopyFrom(ms, sz);
      temp.Position := 0;
      jps[i].LoadFromStream(temp);

      Form1.PaintBox.Canvas.Draw(0, 0, jps[i]); // Show Picture with  Error
      //Form1.Image1.Picture.Bitmap.LoadFromStream(temp);
      Sleep(2000); // To see the Picture for 2 seconds (Test)
    end;

  finally
    ms.Free;
    temp.Free;
    for i := 0 to n - 1 do
        jps[i].Free;
  end;
end.

更新2: 我将代码放在 ButtomClick 过程中并更改:

  temp.Position := 0;
  jps[i].LoadFromStream(temp);

  Form1.PaintBox.Canvas.Draw(0, 0, jps[i]);  // Show Picture in Paintbox
  Form1.Image1.Picture.Assign(jps[i]); // Show Picture in TImage
  Application.ProcessMessages;
  Sleep(500); // To see the Picture for 2 seconds (Test)
end;

我还将使用 Master-Detail Table 或 TFDMemTable 或 Encoding to Text 来测试其他想法,看看哪个最适合我。 感谢所有帮助者。

All Images in the Array has the same Size - 也许维度 (width/height) 相同,但 jpeg 图像的大小(以字节为单位)因图片和压缩而异。所以我假设你需要存储图像数据和数据大小。

可能的方法:

  • 将整数值 - 数组长度 - 写入内存流 ms
  • 将每个数组元素保存到临时内存流temp_ms
  • 将整数值sz = ms.size写入主内存流ms
  • temp_ms.Position 重置为 0
  • 复制 (CopyFrom) sz 个字节,从 temp_msms
  • 清楚temp_ms
  • 对所有数组元素重复

现在 ms 内容如下所示:

10        14212   FFD8....D9     31234   FFD8....D9   
10 images 
         1st image size
                   1st image contents 
                                 2nd image size
                                         2nd image contents
                                       

要检索图像,执行反向操作:

  • 重置 ms 位置(如果需要)
  • ms
  • 中读取图像数量 n
  • 设置数组大小(如果数组是动态的)
  • n次:
  • 读取图片大小sz
  • sz 字节复制到 temp_ms
  • 重置temp_ms.Position
  • temp_ms
  • 加载 array[i]

工作示例:

var
  ms, temp: TMemoryStream;
  jps: array of TJpegImage;
  i, n, sz: Integer;
begin
  n := 3;
  SetLength(jps, n);
  ms := TMemoryStream.Create;
  temp := TMemoryStream.Create;

  for i := 0 to n - 1 do begin
    jps[i] := TJpegImage.Create;
    jps[i].LoadFromFile(Format('h:\%s.jpg', [Chr(Ord('a') + i)]));
    //in my case loads files a.jpg, b.jpg, c.jpg
  end;

  try
    ms.Write(n, SizeOf(n));
    for i :=0 to n - 1 do begin

      temp.Clear;
      jps[i].SaveToStream(temp); //jpeg data here

      sz := temp.Size;
      ms.Write(sz, SizeOf(sz));   //size of image

      temp.Position := 0;
      ms.CopyFrom(temp, sz);     //image data to final destination
    end;

    ms.SaveToFile('h:\base.dat');  //for control

    //revert to empty stream
    ms.Clear;
    ms.LoadFromFile('h:\base.dat');  //load from "base"

    ms.Read(n, SizeOf(n));
    //here some actions to setup array

    for i :=0 to n - 1 do begin

      ms.Read(sz, SizeOf(sz));

      temp.Clear;
      temp.CopyFrom(ms, sz);
      temp.Position := 0;
      jps[i].LoadFromStream(temp);

      Canvas.Draw(i * 200, 0, jps[i]); //control shot
    end;

  finally
    ms.Free;
    temp.Free;
    for i := 0 to n - 1 do
       jps[i].Free;
  end;

你为什么不使用 TFDMemTable?

另一个选项可以是,TStringList使用System.NetEncoding.TNetEncoding.Base64编码 因为 TStringList 非常像一个超级向量