将 base64 编码的数据从 INI 文件加载回 TPicture?
Load base64-encoded data from INI file back to TPicture?
在 Delphi 10.4 中,我已使用以下代码成功地将有效的 TPicture
base64 编码保存到 INI 文件中:
procedure TForm1.SavePictureToIniFile(const APicture: TPicture);
//
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! (发送 TPicture
和 CodeSite.Send
通常有效,在这种情况下,AV 显然意味着图片已损坏)。
那么如何将数据从 INI 文件加载回 TPicture
?
这基本上与原始问题中的问题相同。
INI文件的数据是二进制图像的Base64表示,即字符串。因此,您需要读取此 Base64 字符串并使用 Base64Enc
.
将其转换为二进制 blob
但是你的代码使用了ReadBinaryStream
方法,它把文本当做十六进制字节序列而不是Base64字符串,returns它当做二进制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;
您可能已经意识到这一点的一种方法是这样思考:
如何编码?嗯,我愿意
Base64Enc.EncodeBytesToString
MyIni.WriteString
因此,为了解码,我以相反的顺序执行相反的程序:
MyIni.ReadString
Base64Enc.DecodeStringToBytes
删除不必要的副本
在评论中,Remy Lebeau 正确地指出上面的代码执行了不必要的 in-memory 二进制图像数据副本。虽然这在实践中不太可能成为问题(甚至是可衡量的!),但考虑到我们正在从 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 10.4 中,我已使用以下代码成功地将有效的 TPicture
base64 编码保存到 INI 文件中:
procedure TForm1.SavePictureToIniFile(const APicture: TPicture);
//
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! (发送 TPicture
和 CodeSite.Send
通常有效,在这种情况下,AV 显然意味着图片已损坏)。
那么如何将数据从 INI 文件加载回 TPicture
?
这基本上与原始问题中的问题相同。
INI文件的数据是二进制图像的Base64表示,即字符串。因此,您需要读取此 Base64 字符串并使用 Base64Enc
.
但是你的代码使用了ReadBinaryStream
方法,它把文本当做十六进制字节序列而不是Base64字符串,returns它当做二进制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;
您可能已经意识到这一点的一种方法是这样思考:
如何编码?嗯,我愿意
Base64Enc.EncodeBytesToString
MyIni.WriteString
因此,为了解码,我以相反的顺序执行相反的程序:
MyIni.ReadString
Base64Enc.DecodeStringToBytes
删除不必要的副本
在评论中,Remy Lebeau 正确地指出上面的代码执行了不必要的 in-memory 二进制图像数据副本。虽然这在实践中不太可能成为问题(甚至是可衡量的!),但考虑到我们正在从 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;