delphi - 将 INI 文件中的 base64 编码数据加载回 TPicture?

标签 delphi stream base64 ini delphi-10.4-sydney

在 Delphi 10.4 中,我已使用以下代码成功将有效的 TPicture base64 编码保存到 INI 文件中:

procedure TForm1.SavePictureToIniFile(const APicture: TPicture);
// https://stackoverflow.com/questions/63216011/tinifile-writebinarystream-creates-exception
var
  LInput: TMemoryStream;
  MyIni: TMemIniFile;
  Base64Enc: TBase64Encoding;
  ThisFile: string;
begin
  if FileSaveDialog1.Execute then
    ThisFile := FileSaveDialog1.FileName
  else EXIT;

  //CodeSite.Send('TForm1.btnSaveToIniClick: VOR Speichern');
  LInput := TMemoryStream.Create;
  try
    APicture.SaveToStream(LInput);
    LInput.Position := 0;
    MyIni := TMemIniFile.Create(ThisFile);
    try
      Base64Enc := TBase64Encoding.Create(Integer.MaxValue, '');
      try
        MyIni.WriteString('Custom', 'IMG', Base64Enc.EncodeBytesToString(LInput.Memory, LInput.Size));
      finally
        Base64Enc.Free;
      end;
      MyIni.UpdateFile;
    finally
      MyIni.Free;
    end;
  finally
    LInput.Free;
  end;
  //CodeSite.Send('TForm1.btnSaveToIniClick: NACH Speichern'); // 0,024 Sek.
end;

现在我想反转此过程,即将数据从 INI 文件加载回 TPicture:

procedure TForm1.btnLoadFromIniClick(Sender: TObject);
var
  LInput: TMemoryStream;
  LOutput: TMemoryStream;
  ThisFile: string;
  MyIni: TMemIniFile;
  Base64Enc: TBase64Encoding;
  ThisEncodedString: string;
  ThisPicture: TPicture;
begin
  if FileOpenDialog1.Execute then
    ThisFile := FileOpenDialog1.FileName
  else EXIT;

  MyIni := TMemIniFile.Create(ThisFile);
  try
    Base64Enc := TBase64Encoding.Create(Integer.MaxValue, '');
    try
      (*ThisEncodedString := MyIni.ReadString('Custom', 'IMG', '');
      Base64Enc.Decode(ThisEncodedString); // And now???*)
      LInput := TMemoryStream.Create;
      LOutput := TMemoryStream.Create;
      try
        MyIni.ReadBinaryStream('Custom', 'IMG', LInput);
        MyIni.UpdateFile;
        LInput.Position := 0;
        Base64Enc.Decode(LInput, LOutput);
        LOutput.Position := 0;

        ThisPicture := TPicture.Create;
        try
          ThisPicture.LoadFromStream(LOutput);
          CodeSite.Send('TForm1.btnLoadFromIniClick: ThisPicture', ThisPicture); // AV!
        finally
          ThisPicture.Free;
        end;
      finally
        LOutput.Free;
        LInput.Free;
      end;
    finally
      Base64Enc.Free;
    end;        
  finally
    MyIni.Free;
  end;
end;

但是当使用 CodeSite.Send 发送图片时会创建 AV! (使用 CodeSite.Send 发送 TPicture 通常是有效的,在这种情况下,AV 显然意味着图片已损坏)。

那么如何将数据从 INI 文件加载回 TPicture

最佳答案

这本质上与原始问题中的问题相同。

INI 文件的数据是二进制图像的 Base64 表示,即一个字符串。因此,您需要读取此 Base64 字符串并使用 Base64Enc 将其转换为二进制 blob。

但是您的代码使用 ReadBinaryStream 方法,该方法不会将文本视为 Base64 字符串,而是将其视为十六进制字节序列,并将其作为二进制 blob 返回,然后将其提供给 Base64Enc

这样做:

var
  ImgData: TBytes;
begin
  MyIni := TMemIniFile.Create('D:\img.ini');
  try
    Base64Enc := TBase64Encoding.Create(Integer.MaxValue, '');
    try
      LInput := TMemoryStream.Create;
      try
        ImgData := Base64Enc.DecodeStringToBytes(MyIni.ReadString('Custom', 'IMG', ''));
        LInput.WriteData(ImgData, Length(ImgData));
        LInput.Position := 0;
        ThisPicture := TPicture.Create;
        try
          ThisPicture.LoadFromStream(LInput);
          // Use ThisPicture
        finally
          ThisPicture.Free;
        end;
      finally
        LInput.Free;
      end;
    finally
      Base64Enc.Free;
    end;
  finally
    MyIni.Free;
  end;

您可能意识到这一点的一种方法是这样思考:

如何编码?嗯,我愿意

  1. Base64Enc.EncodeBytesToString
  2. MyIni.WriteString

因此,为了解码,我以相反的顺序执行相反的过程:

  1. MyIni.ReadString
  2. Base64Enc.DecodeStringToBytes

删除不必要的副本

评论中,Remy Lebeau正确指出上面的代码对二进制图像数据执行了不必要的内存复制。尽管这在实践中不太可能成为问题(甚至是可测量的!),但考虑到我们是从 INI 文件中的 Base64 编码字段读取图像,但它仍然是浪费且丑陋的。

通过将 TMemoryStream 替换为 TBytesStream(TMemoryStream 的后代),我们可以将 Base64 数据直接解码到流中:

var
  ImgStream: TBytesStream;
begin
  MyIni := TMemIniFile.Create('D:\img.ini');
  try
    Base64Enc := TBase64Encoding.Create(Integer.MaxValue, '');
    try
      ImgStream := TBytesStream.Create(Base64Enc.DecodeStringToBytes(MyIni.ReadString('Custom', 'IMG', '')));
      try
        ThisPicture := TPicture.Create;
        try
          ThisPicture.LoadFromStream(ImgStream);
          // Use ThisPicture
        finally
          ThisPicture.Free;
        end;
      finally
        ImgStream.Free;
      end;
    finally
      Base64Enc.Free;
    end;
  finally
    MyIni.Free;
  end;

关于delphi - 将 INI 文件中的 base64 编码数据加载回 TPicture?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63226950/

相关文章:

图像/表单到 Pascal/Delphi 代码转换器?

delphi - 如何在Delphi中向另一个exe添加版本信息?

c# - 如何使用 p/invoke 编码 Delphi 短字符串?

asynchronous - 如何创建一个流,其中项目基于流之前返回的项目?

java - OneTimePad 实现失败。也许是流问题?

Delphi ListView OnSelectItem 触发两次

c++ - 关于使用iostream进行解析的准则是什么?

java - 错误: Bad Base64Coder input character

ios - 将base64解码为uiimage iOS Swift

JavaScript 用 Base64 替换 Textarea 文本