Stream objects to a file using TFileStream - delphi

Why does this code not work?
I am writing an application that has ability to save and load its own files and need to know how to stream objects to a file using FileStream.
procedure TForm1.btnSaveClick(Sender: TObject);
var
fs: TFileStream;
begin
fs := TFileStream.Create('c:\temp\a.my', fmCreate);
try
fs.WriteBuffer(Image1.Picture.Graphic, SizeOf(TGraphic));
finally
fs.Free;
end;
ShowMessage('ok');
Image1.Picture.Graphic := nil;
end;
procedure TForm1.btnLoadClick(Sender: TObject);
var
fs: TFileStream;
g: TGraphic;
begin
fs := TFileStream.Create('c:\temp\a.my', fmOpenRead);
try
fs.ReadBuffer(g, SizeOf(TGraphic));
Image1.Picture.Graphic := g;
finally
fs.Free;
end;
ShowMessage('ok');
end;
EDIT 1:
Found the way to do it, but need some more help:
procedure TForm1.btnSaveClick(Sender: TObject);
var
fs: TFileStream;
s: TMemoryStream;
buf: TBytes;
begin
fs := TFileStream.Create('c:\temp\a.my', fmCreate);
s := TMemoryStream.Create;
try
Image1.Picture.Graphic.SaveToStream(s);
SetLength(buf, s.Size);
s.Position := 0;
s.ReadBuffer(buf[0], s.Size);
//fs.WriteBuffer(, SizeOf(Integer)); <-here how do I save an integer which represents the size of the buffer? (so that when reading back i read this first.)
fs.WriteBuffer(buf[0], s.Size);
finally
s.Free;
fs.Free;
end;
ShowMessage('ok');
Image1.Picture.Graphic := nil;
end;

What you have done there is stream the reference, i.e. a pointer. What you need to stream is the contents. You can that with SaveToFile and LoadFromFile.
Regarding your update, assign s.Size to a local variable of type Integer and then use WriteBuffer to save it. In reverse, use ReadBuffer to read into a local variable.
If I were you I would write direct to the file and avoid the memory streak. Use the Position property of TStream to seek around the file. So write 0 for then length, write the graphic, seek back to the beginning and write the true length accounting for the 4 bytes of the length.

Related

Using TIdHTTP to download file using multiple threads [duplicate]

I have two binary files (suppose, this is a ZIP-file previously sliced into 2 parts). How do I can combine them into a single file? More precisely, add the second file to the first one.
UPDATE: Guys, thanks to everyone who responded me, but it's not exactly what I need. Basically, I need an analogue of the shell command: "copy /b file.000+file.001+file.002 file.bin"
Like this:
var
InStream, OutStream: TFileStream;
....
OutStream := TFileStream.Create(OutFileName, fmCreate);
try
InStream := TFileStream.Create(InFileName1, fmOpenRead);
try
OutStream.CopyFrom(InStream, InStream.Size);
finally
InStream.Free;
end;
InStream := TFileStream.Create(InFileName2, fmOpenRead);
try
OutStream.CopyFrom(InStream, InStream.Size);
finally
InStream.Free;
end;
finally
OutStream.Free;
end;
Or more generally to concatenate multiple files:
procedure ConcatenateFiles(const InFileNames: array of string;
const OutFileName: string);
var
i: Integer;
InStream, OutStream: TFileStream;
begin
OutStream := TFileStream.Create(OutFileName, fmCreate);
try
for i := 0 to high(InFileNames) do
begin
InStream := TFileStream.Create(InFileNames[i], fmOpenRead);
try
OutStream.CopyFrom(InStream, InStream.Size);
finally
InStream.Free;
end;
end;
finally
OutStream.Free;
end;
end;
Call it like this:
ConcatenateFiles(['Part1', 'Part2'], 'Outfile.zip')

How to get the pointer position of beginning of second line using TFileStream in Delphi?

I'm trying to create a list of pointers to store pointers of beginning of each line using TFileStream in Delphi.
I have gone through the following link TFileStream_Methods to check all the methods. but i didn't find anything useful using which i can find the pointer value of starting position of each line.
I have tried following :
procedure TForm4.Button3Click(Sender: TObject);
var
InStream: TFileStream;
OutStream: TMemoryStream;
Writer: TStreamWriter;
Buffer: TArray<Byte>;
Encoding: TEncoding;
BOMValue: Integer;
FileName: string;
Temp: string;
TempInt: Integer;
//Buffer2: Buffer;
begin
try
//SetLength(Buffer, 8);
//InStream.Read(Buffer, Length(Buffer));
finally
//InStream.Free;
end;
try
SetLength(Buffer, 8);
//InStream.Read(Buffer, Length(Buffer));
finally
//InStream.Free;
end;
Encoding := nil;
BOMValue := TEncoding.GetBufferEncoding(Buffer, Encoding, TEncoding.ASCII);
OutStream := TMemoryStream.Create;
Writer := TStreamWriter.Create(OutStream, Encoding);
FileName := 'C:\bugs\Rnd\mainfile.cpp';
try
InStream := TFileStream.Create(FileName, fmOpenRead);
InStream.Seek(BOMValue, soCurrent);
Temp := InStream.ToString();
TempInt := InStream.InstanceSize();
OutStream.CopyFrom(InStream, 10);
InStream.Seek(10, soCurrent);
Writer.Write('abc');
OutStream.CopyFrom(InStream,60);
InStream.Free;
OutStream.SaveToFile(FileName);
finally
//InStream.Free;
Writer.Free;
OutStream.Free;
end;
end;
I am having the InStream, how can I read it character by character (even multibyte character) and can calculate the pointer of the first position of each line.
I have been trying for past 2 days but didn't have any luck. Any help would he highly appreciated.

Using TIdCompressorLib to Decompress gzip file

I am using Delphi 2007 with Indy 10. I have a gzip file. I have verified it can be decompressed with this Online Decompression Tool.
I am trying to use the TIdCompressorZlib component to decompress using Delphi. Here is my code:
procedure TForm2.Button9Click(Sender: TObject);
var
lCompressor : TIdCompressorZLib;
FileStream : TFileStream;
memorystream: TMemoryStream;
begin
lCompressor := TIdCompressorZLib.create(self);
FileStream := TFileStream.Create('c:\temp\test.gz', fmOpenRead);
filestream.position := 0;
memorystream:= TMemoryStream.create;
memorystream.position := 0;
lcompressor.DecompressGZipStream(FileStream,MemoryStream);
filestream.free;
showmessage('done');
end;
I cannot get it to work. If I pass fmOpenReadWrite in the constructor I get a zlib error (-5) when DecompressGZipStream is called.
If I pass fmOpenRead in the constructor I get a OS System Error Code 5 Access Denied when DecompressGZipStream is called.
Update David Hefferan suggested it is a file reading issue. So I am zeroing in on that. I am able to copy the file using this procedure:
Procedure FileCopy( Const sourcefilename, targetfilename: String );
Var
S, T: TFileStream;
Begin
S := TFileStream.Create( sourcefilename, fmOpenRead );
try
T := TFileStream.Create( targetfilename,
fmOpenWrite or fmCreate );
try
T.CopyFrom(S, S.Size ) ;
finally
T.Free;
end;
finally
S.Free;
end;
showmessage('done');
End;
UPD Per David Heffernan, I have verified I can Read the data. I successfully ran the file through the following function. It returns the proper number of characters (bytes):
function GetTextFromFile(AFile: string; var Returnstring: string): Boolean;
var
FileStream: TFileStream;
begin
Result := False;
if not FileExists(AFile) then Exit;
FileStream := TFileStream.Create(AFile, fmOpenRead);
try
if FileStream.Size <> 0 then
begin
SetLength(Returnstring, FileStream.Size);
FileStream.Read(Returnstring[1], FileStream.Size);
Result := True;
end;
finally
FileStream.Free;
end;
end;
You can try to use Delphi zlib unit to decompress gzip.
Sample code:
procedure TForm2.Button9Click(Sender: TObject);
var
DecompressionStream : TDecompressionStream;
FileStream : TFileStream;
dest: TFileStream;
byteCount: Integer;
buffer: array [0..65535] of Byte;
begin
FileStream := TFileStream.Create('c:\temp\test.gz', fmOpenRead);
try
//no need to set FileStream position to 0, it's there already
DecompressionStream:=TDecompressionStream.Create(FileStream, 15+16);
//16 is flag that gzip stream used, not zlib.
//15 is maximum memory usage, to speed-up decompression.
try
dest:=TFileStream.Create('c:\temp\test.txt', fmCreate);
try
dest.CopyFrom(DecompressionStream,0);
finally
dest.free;
end;
finally
DecompressionStream.free;
end;
finally
filestream.free;
end;
showmessage('done');
end;
UPD: this code doesn't work for D2007 or earlier versions, no overloaded constructor with WindowBits argument...

How to load a stream with a file copied in Windows clipboard

I've copied a file into the Windows clipboard (By simply clicking right, copy).
I would like to load a TStream descendant with the file currently stored in the clipboard.
uses
Classes, Clipbrd;
MyStream := TMemoryStream.Create;
try
//here I would like to load the clipboard file into MyStream
finally
MyStream.Free;
end;
When you copy a file onto the clipboard from the hard drive, it simply copies the file's full path and filename in CF_HDROP format. You can use the DragQueryFile() function to read the filenames, eg:
uses
Classes, Clipbrd, ShellAPI;
var
hDrop: THandle
MyStream: TMemoryStream;
Files: TStringList;
NumFiles, FileIdx: DWORD;
FileName: array[0..MAX_PATH] of Char;
I: Integer;
begin
Files := TStringList.Create;
try
Clipboard.Open;
try
if Clipboard.HasFormat(CF_HDROP) then
begin
// DO NOT free this handle, the clipboard owns it!
hDrop := Clipboard.GetAsHandle(CF_HDROP);
NumFiles := DragQueryFile(hDrop, $FFFFFFFF, nil, 0);
if NumFiles <> 0 then
begin
for FileIdx := 0 to NumFiles-1 do
begin
if DragQueryFile(hDrop, FileIdx, FileName, MAX_PATH) <> 0 then
Files.Add(FileName);
end;
end;
end;
finally
Clipboard.Close;
end;
for I := 0 to Files.Count-1 do
begin
MyStream := TMemoryStream.Create;
try
MyStream.LoadFromFile(Files[I]);
MyStream.Position := 0;
// use MyStream as needed...
finally
MyStream.Free;
end;
end;
finally
Files.Free;
end;
end;

Delphi - Reading from a log file that changes every second

I need to read from a .log file that is constantly changing by another application. (more data being added frequently)
So I have this to begin with:
var
LogFile: TStrings;
Stream: TStream;
begin
LogFile := TStringList.Create;
try
Stream := TFileStream.Create(Log, fmOpenRead or fmShareDenyNone);
try
LogFile.LoadFromStream(Stream);
finally
Stream.Free;
end;
while LogFile.Count > Memo1.Lines.Count do
Memo1.Lines.Add(LogFile[Memo1.Lines.Count]);
finally
LogFile.Free;
end;
end;
This works perfectly fine. It updates the memo at real time with the data being added. However some of the data being added I don't want to see in the memo. I wish to not add these lines, but still have the memo updated at real time without the junk lines.
What is the best way to go about this?
You'd clearly need to check to see if the line has content you want to include, and only add it if it has that content (or not add it if you don't want to include it, whichever is the case). It would also be much more efficient to keep track of the last line in the LogFile you processed previously, so you could skip those lines each time - if you make the variable a private member of the form itself, it will automatically be initialized to 0 when your application starts:
type
TForm1 = class(TForm)
//... other stuff added by IDE
private
LastLine: Integer;
end;
// At the point you need to add the logfile to the memo
for i := LastLine to LogFile.Count - 1 do
begin
if ContentWanted(LogFile[i]) then
Memo1.Lines.Append(LogFile[i]);
Inc(LastLine);
end;
So to handle this completely based on your code:
type
TForm1 = class(TForm)
//... IDE stuff here
private
FLastLogLine: Integer;
procedure ProcessLogFile;
public
// Other stuff
end;
procedure TForm1.ProcessLogFile;
var
Log: TStringList;
LogStream: TFileStream;
i: Integer;
begin
Log := TStringList.Create;
try
LogStream := TFileStream.Create(...);
try
Log.LoadFromStream(LogStream);
finally
LogStream.Free;
end;
for i := FLastLogLine to Log.Count - 1 do
if Pos('[Globals] []', Log[i]) <>0 then
Memo1.Lines.Append(Log[i]);
// We've now processed all the lines in Log. Save
// the last line we processed as the starting point
// for the next pass.
FLastLogLine := Log.Count - 1;
finally
Log.Free;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
try
ProcessLogFile;
finally
Timer1.Enabled := True;
end;
end;
end;
I know its been a couple of weeks since i last posted here, but i rewrote the entire application and came up with this piece of code, which is working perfectly!
Basically i do not call .free on the stream or stringlist and am able to set the initial stream size then check if its changed, hence getting the data i need and not the entire file!
Thanks everyone for helping!
procedure TForm1.GetEndLogFile;
begin
LogFile := TStringList.Create;
Stream := TFileStream.Create('C:\Users\John\Documents\chat.log', fmOpenRead or fmShareDenyNone);
LogFile.LoadFromStream(Stream);
i := Stream.Size;
end;
procedure TForm1.LogFileRefresh;
var
buf: string;
begin
if i <> Stream.Size then
begin
SetLength(buf, Stream.Size);
Stream.Seek(i, Stream.Size);
Stream.Read(buf[1], Stream.Size);
i := Stream.Size;
Memo1.Lines.Append(Buf);
//ShowMessage(buf);
end;
end;
procedure TForm1.GetEndLogFile;
var
LogFile: TStrings;
Stream: TStream;
begin
LogFile := TStringList.Create;
try
Stream := TFileStream.Create(LogFile, fmOpenRead or fmShareDenyNone);
try
LogFile.LoadFromStream(Stream);
EndOfFile := LogFile.Count;
finally
Stream.Free;
end;
finally
LogFile.Free;
end;
end;
procedure TForm1.LogFileRefresh;
var
LogFile2: TStrings;
Stream2: TStream;
i: Integer;
begin
LogFile2 := TStringList.Create;
try
Stream2 := TFileStream.Create(LogFile, fmOpenRead or fmShareDenyNone);
try
LogFile2.LoadFromStream(Stream2);
finally
Stream2.Free;
end;
for i := EndOfFile to LogFile2.Count -1 do
begin
if Pos('[Globals] []',LogFile2[i])<>0 then
Memo1.Lines.Append(LogFile2[i]);
Inc(EndOfFile);
end;
finally
LogFile2.Free
end;
end;
Basically came up with this, and its working perfectly fine. Should i run into any problems this way? Is there a neater way to do this?

Resources