I have an image, a string, and an integer that i need to save/read as a file. What would be the best way to do this. I was thinking a TStream, but I am unsure of how to implement it.
You could use Delphi's built-in component streaming support and save it either as binary or text data. One disadvantage with this method is if your images are high-resolution then you might be punished in greater file-size when dumping the in-memory-pixel-data to disk compared to the image's original file-format, e.g. jpeg. In my example below I derive a class from TImage
and add a string value, MyString
and an integer, MyInteger
and save as binary data.
Stream Component to/from a binary-file
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
type
TMyImage = class(TImage)
private
FString : string;
FInteger : integer;
public
published
property MyString: string read FString write FString;
property MyInteger: Integer read FInteger write FInteger;
end;
TFormMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
FMyImage : TMyImage;
FMyImage2 : TMyImage;
public
end;
var
FormMain: TFormMain;
implementation
{$R *.dfm}
procedure WriteComponent(Component: TComponent; Filename : String);
var
m :TMemoryStream;
begin
m := TMemoryStream.Create;
try
m.WriteComponent(Component);
m.Seek(0, soFromBeginning);
m.SaveToFile(Filename);
finally
m.Free;
end;
end;
function ReadComponent(Filename : String): TComponent;
var
m :TMemoryStream;
begin
m := TMemoryStream.Create;
try
m.LoadFromFile(FileName);
m.Seek(0, soFromBeginning);
result := m.ReadComponent(nil);
finally
m.Free;
end;
end;
procedure TFormMain.FormCreate(Sender: TObject);
begin
FMyImage := TMyImage.Create(Self);
FMyImage.Picture.LoadFromFile('C:\...\Delphi\Common Images\ALIGNPAL.BMP');
FMyImage.MyString := 'Hello World';
FMyImage.MyInteger := 12345;
WriteComponent(FMyImage, 'c:\test.dat');
FMyImage2 := ReadComponent('c:\test.dat') as TMyImage;
end;
initialization
RegisterClass(TMyImage);
end.
A simplified approach
If you want to get by with only one String and one Integer than you could use TImage's Caption and Tag properties.
...
FImage : TImage;
FImage2 : TImage;
....
procedure TFormMain.FormCreate(Sender: TObject);
begin
FImage := TImage.Create(Self);
FImage.Picture.LoadFromFile('C:\...\Delphi\Common Images\ALIGNPAL.BMP');
FImage.Caption := 'Hello World';
FImage.Tag := 12345;
WriteComponent(FImage, 'c:\test.dat');
FImage2 := ReadComponent('c:\test.dat') as TImage;
end;
Stream Component to/from text
Below I'v also added useful helper functions, found here, if you want more "readable" data-files by making string representations of the binary-data. File size will however increase.
function StringToComponentProc(Value: string): TComponent;
var
StrStream:TStringStream;
BinStream: TMemoryStream;
begin
StrStream := TStringStream.Create(Value);
try
BinStream := TMemoryStream.Create;
try
ObjectTextToBinary(StrStream, BinStream);
BinStream.Seek(0, soFromBeginning);
Result:= BinStream.ReadComponent(nil);
finally
BinStream.Free;
end;
finally
StrStream.Free;
end;
end;
function ComponentToStringProc(Component: TComponent): string;
var
BinStream:TMemoryStream;
StrStream: TStringStream;
s: string;
begin
BinStream := TMemoryStream.Create;
try
StrStream := TStringStream.Create(s);
try
BinStream.WriteComponent(Component);
BinStream.Seek(0, soFromBeginning);
ObjectBinaryToText(BinStream, StrStream);
StrStream.Seek(0, soFromBeginning);
Result:= StrStream.DataString;
finally
StrStream.Free;
end;
finally
BinStream.Free
end;
end;