Issue with loading png image from inifile - delphi

I store PNG image into Ini file with the following code :
procedure TfrmParametres.SaveIni;
var
IniFile: TIniFile;
MS: TMemoryStream;
PNG: TPngImage;
begin
IniFile := TIniFile.Create(IniFileName);
try
PNG := TPngImage.Create;
try
PNG.LoadFromFile(edtLogo.Text);//edtlogo contain image file path
MS := TMemoryStream.Create;
try
PNG.SaveToStream(MS);
MS.Seek(0, 0);
IniFile.WriteBinaryStream('REPORT_HEADER', 'LOGO', MS);
finally
MS.Free;
end;
finally
PNG.Free;
end;
finally
FreeAndNil(IniFile);
end;
end;
and to show the picture in another form OnShow event I used the same approach :
Load DATA in TMemoryStream object
Load DATA from MemoryStream into TPngImage object
Show the picture in TImage component
procedure TfrmLoadPicture.FormShow(Sender: TObject);
var
IniFile: TIniFile;
MS: TMemoryStream;
PNG: TPngImage;
begin
IniFile:= TIniFile.Create(frmParametres.IniFileName);
try
MS:= TMemoryStream.Create;
try
IniFile.ReadBinaryStream('REPORT_HEADER', 'LOGO', MS);
PNG := TPngImage.Create;
try
MS.Seek(0, 0);
PNG.LoadFromStream(MS);
Image.Picture.Assign(PNG);
finally
PNG.Free;
end;
finally
MS.Free;
end;
finally
IniFile.Free;
end;
end;
however I always get Exception error:

TIniFile has a hard-coded cap on the size of any one value it can read, partly due to the fact the underlying Windows API (GetPrivateProfileString and friends) doesn't allow querying the size of a saved value. IMO TIniFile should really raise an exception on an attempt to write a larger value, but regardless, if you use TMemIniFile instead of TIniFile you should be fine (I've just tried it).

Chris has answered the direct question that you asked. I have some other comments.
No need to decode the PNG to transfer it to the INI file
You can copy the file directly without decoding the PNG and then recoding.
Stream := TFileStream.Create(FileName, fmOpenRead);
try
IniFile.WriteBinaryStream('REPORT_HEADER', 'LOGO', Stream);
finally
Stream.Free;
end;
Hexadecimal encoding is not efficient
WriteBinaryStream is not a very efficient way to encode binary as text. In effect you are using base16 and it would be more conventional and efficient to use base64.
I suggest that you encode the binary file stream to a base64 string and write that string to the INI file.
INI files are not suited to binary data
INI files were never intended to be used to store large binary blobs. On first inspection it would seem odd that you are trying to shoe horn a PNG image into an INI file.

Related

Saved data from TBlobField is corrupted for lengths >= 100KB

I'm modifying a program that is written in Delphi 6.0
I have a table in Oracle with a BLOB column named FILE_CONTENT.
I have already managed to upload an XML File that is about 100 KB. I have verified that the file content was correctly uploaded using SQL Developer.
The problem I have is when I try to download back the file content from DB to a file. This is an example code I'm using to donwload it:
procedure TfrmDownload.Save();
var
fileStream: TFileStream;
bField: TBlobField;
begin
dmDigital.qrGetData.Open;
dmDigital.RequestLive := True;
bField := TBlobField(dmDigital.qrGetData.FieldByName('FILE_CONTENT'));
fileStream := TFileStream.Create('FILE.XML', fmCreate);
bField.SaveToStream(fileStream);
FlushFileBuffers(fileStream.Handle);
fileStream.Free;
dmDigital.qrGetData.Close;
end;
The previous code already downloads the file content to FILE.XML. I'm using RequestLive:=True to be able to download a large BLOB (otherwise the file content is truncated to 32K max)
The resulting file is the same size as the original file. However, when I compare the downloaded file with the original one there are some differences (for example the last character is missing and other characters are also changed), therefore it seems to be a problem while downloading the content.
Do you know what cuould be wrong?
The problem seems to be related to Delphi code because I already tried with C# and the file content is downloaded correctly.
Don't use TBlobField.SaveToStream() directly, use TDataSet.CreateBlobStream() instead (which is what TBlobField.SaveToStream() uses internally anyway):
procedure TfrmDownload.Save;
var
fileStream: TFileStream;
bField: TField;
bStream: TStream;
begin
dmDigital.qrGetData.Open;
try
dmDigital.RequestLive := True;
bField := dmDigital.qrGetData.FieldByName('FILE_CONTENT');
bStream := bField.DataSet.CreateBlobStream(bField, bmRead);
try
fileStream := TFileStream.Create('FILE.XML', fmCreate);
try
fileStream.CopyFrom(bStream, 0);
FlushFileBuffers(fileStream.Handle);
finally
fileStream.Free;
end;
finally
bStream.Free;
end;
finally
dmDigital.qrGetData.Close;
end;
end;
TDataSet.CreateBlobStream() allows the DataSet to decide the best way to access the BLOB data. If the returned TStream is not delivering the data correctly, then either the TStream class implementation that CreateBlobStream() uses is broken, or the underlying DB driver is buggy. Try taking CopyFrom() out of the equation so you can verify the data as it is being retrieved:
procedure TfrmDownload.Save;
const
MaxBufSize = $F000;
var
Buffer: array of Byte;
N: Integer;
fileStream: TFileStream;
bField: TField;
bStream: TStream;
begin
dmDigital.qrGetData.Open;
try
dmDigital.RequestLive := True;
bField := dmDigital.qrGetData.FieldByName('FILE_CONTENT');
bStream := bField.DataSet.CreateBlobStream(bField, bmRead);
try
fileStream := TFileStream.Create('FILE.XML', fmCreate);
try
//fileStream.CopyFrom(bStream, 0);
SetLength(Buffer, MaxBufSize);
repeat
N := bStream.Read(PByte(Buffer)^, MaxBufSize);
if N < 1 then Break;
// verify data here...
fileStream.WriteBuffer(PByte(Buffer)^, N);
until False;
FlushFileBuffers(fileStream.Handle);
finally
fileStream.Free;
end;
finally
bStream.Free;
end;
finally
dmDigital.qrGetData.Close;
end;
end;

How to set text dfm value to checked on all dfm files/Count number of lines in .dfm binary file

with below example i am counting number of lines in .dfm file and the count is coming wrong because .dfm is saved in binary format.
if i open .dfm file and do right click and set text dfm to checked and the count is coming correctly. Below is the code
function TForm1.FindNumberOfLinesInFile(FileName: String): Integer;
var
contents : TStringList;
filestream : TFileStream;
outStream : TMemoryStream;
begin
try
try
Result := 0;
contents := TStringList.Create;
if edtFileToSearch.Text = '.dfm' then
begin
contents.LoadFromFile(FileName);
//i am binary
if pos('OBJECT', Uppercase(contents[0])) = 0 then // Count is coming wrong with this
begin
contents.Clear;
fileStream := TFileStream.Create(FileName, fmShareDenyNone);
outStream := TMemoryStream.Create;
try
ObjectResourceToText(filestream,outStream);
outStream.Position := 0;
Contents.LoadFromStream(outStream);
finally
FreeAndNil(outStream);
end;
end
else
begin
fileStream := TFileStream.Create(FileName, fmShareDenyNone);
Contents.LoadFromStream(fileStream);
end;
end
else
begin
fileStream := TFileStream.Create(FileName, fmShareDenyNone);
Contents.LoadFromStream(filestream);
end;
Result := contents.Count;
finally
FreeAndNil(fileStream);
FreeAndNil(contents);
end;
except
on e: Exception do Result := -1;
end;
end;
i have 2 questions
1)how to set text dfm value to checked in all dfm files(i have around 1000 dfm files)?
2)how load binary file correctly and count number of lines?
Delphi comes with a command line tool to do this, named convert. Open up a command prompt and ensure that your Delphi bin directory is in the PATH. Then type:
C:\projects\myprocject> convert
The output will be something like this:
Delphi Form Conversion Utility Version 5.0
Copyright (c) 1995,99 Inprise Corporation
Usage: convert.exe [-i] [-s] [-t | -b]
-i Convert files in-place (output overwrites input)
-s Recurse subdirectories
-t Convert to text
-b Convert to binary
So, you should be able to write:
C:\projects\myprocject> convert -i -s -t *.dfm
to effect the change required.
David's answer addresses the first of your questions: You can convert all of your existing binary DFM's to text using the command line tool provided with Delphi.
As well as addressing your immediate problem this is also highly recommended as it will make it much easier (i.e. possible at all!) to visually diff changes to your DFM files in version control.
As for the second part, if for some reason you still want or need to handle binary DFM files in your code is to use the TestStreamFormat() function to determine whether a stream is a valid resource stream and whether it is binary or text format, before calling ObjectResourceToText() function only if required.
This helper function to return the contents of a specified filename (of a DFM) into a supplied TStrings (e.g. a TStringlist) demonstrates this and might simplify things for you:
procedure GetDfmIntoStrings(aFilename: String; aStrings: TStrings);
var
istrm, ostrm: TStream;
begin
ostrm := NIL;
istrm := TFileStream.Create(aFilename, fmOpenRead or fmShareDenyNone);
try
case TestStreamFormat(istrm) of
sofBinary : begin
ostrm := TStringStream.Create('');
ObjectResourceToText(istrm, ostrm)
end;
sofText : ostrm := istrm;
else
raise EFilerError.Create(aFilename + ' is not a valid resource stream (DFM)');
end;
ostrm.Position := 0;
aStrings.LoadFromStream(ostrm);
finally
if ostrm <> istrm then
ostrm.Free;
istrm.Free;
end;
end;

stuck with streaming file to string

okay, so I (VERY) recently started playing with lazaruz/free pascal, and I'm a little stuck with reading files with TMemoryStream and it's streaming kin.
I'm trying to write a simple base64 encoder, that can encode strings of text, or files (like images and WAVs) to then be used in html and javascript.
The following code compiles great but I get EReadError Illegal stream image when trying to load a file. I'll include the working string only procedure for reference:
procedure TForm1.TextStringChange(Sender: TObject);
begin
Memo1.Lines.Text := EncodeStringBase64(TextString.Text);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Lines.Text := '';
Form1.BorderIcons := [biSystemMenu,biMinimize];
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
filename := OpenDialog1.Filename;
stream := TMemoryStream.Create;
try
StrStream := TStringStream.Create(s);
try
stream.LoadFromFile(filename);
stream.Seek(0, soFromBeginning);
ObjectBinaryToText(stream, StrStream);
StrStream.Seek(0, soFromBeginning);
Memo1.Lines.Text := EncodeStringBase64(StrStream.DataString);
finally
StrStream.Free;
end;
finally
stream.Free;
end;
end;
end;
Can anyone help me out?
You get the "illegal stream image" exception because the file you're loading probably isn't a binary DFM file. That's what ObjectBinaryToText is meant to process. It's not for arbitrary data. So get rid of that command.
You can skip the TMemoryStream, too. TStringStream already has a LoadFromFile method, so you can call it directly instead of involving another buffer.
StrStream.LoadFromFile(filename);
But a string isn't really the right data structure to store your file in prior to base64-encoding it. The input to base64 encoding is binary data; the output is text. Using a text data structure as an intermediate format means you may introduce errors into your data because of difficulties in encoding certain data as valid characters. The right interface for your encoding function is this:
function Base64Encode(Data: TStream): string;
You don't need to load the entire file into memory prior to encoding it. Just open the file with a TFileStream and pass it to your encoding function. Read a few bytes from it at a time with the stream's Read method, encode them as base64, and append them to the result string. (If you find that you need them, you can use an intermediate TStringBuilder for collecting the result, and you can add different buffering around the file reads. Don't worry about those right away, though; get your program working correctly first.)
Use it something like this:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
filename: string;
stream: TStream;
begin
if OpenDialog1.Execute then begin
filename := OpenDialog1.Filename;
stream := TFileStream.Create(filename, fmOpenRead);
try
Memo1.Lines.Text := Base64Encode(stream);
finally
stream.Free;
end;
end;
end;
I never heard before about ObjectBinaryToText(), but looks like funky one. Also, what is EncodeStringBase64() function?
At first place, you shouldn't convert binary stream to text to encode it, instead you should directly B64 encode binary data. B64 algorithm is intended to work on array of bytes.
Since Delphi 6, there is EncdDecd.pas unit, which implements B64 encoding methods. I'm not sure if Lazarus/FPC have this, but if they do, your code to B64 encode file should look like this (add EncdDecd to uses list):
procedure TForm1.Button1Click(Sender: TObject);
var
instream : TFileStream;
outstream: TStringStream;
begin
if OpenDialog1.Execute then
begin
instream := TFileStream.Create(OpenDialog1.FileName, fmOpenRead or fmShareDenyNone);
try
outstream := TStringStream.Create;
try
EncodeStream(instream, outstream);
Memo1.Lines.Text := outstream.DataString;
finally
outstream.Free;
end;
finally
instream.Free;
end;
end;
end;

Extracting images from zip into memory delphi

I'm wanting to extract a zip file loaded with images into memory in some way. I don't really care what type of stream they go into, as long as I can load them afterwards. I do not have that great of an understanding with streams, and explanations on the subject don't seem to go into much detail.
Essentially, what I am doing now is extracting the files to (getcurrentdir + '\temp\'). This works, but isn't quite what I am wanting to do. I would be more happy to have the jpg's end up in memory and then be able to read from memory into a TImage.bitmap.
I am currently using jclcompresion to handle zips and rars, but was considering moving back to system.zip because I really only need to be able to handle zip files. If it would be easier to stay with jclcompression though that would work for me.
The read method of the TZipFile class can be used with a stream
procedure Read(FileName: string; out Stream: TStream; out LocalHeader: TZipHeader); overload;
procedure Read(Index: Integer; out Stream: TStream; out LocalHeader: TZipHeader); overload;
from here you can access the compressed file using the index or the filename.
Check this sample which uses a TMemoryStream to hold the uncompressed data.
uses
Vcl.AxCtrls,
System.Zip;
procedure TForm41.Button1Click(Sender: TObject);
var
LStream : TStream;
LZipFile : TZipFile;
LOleGraphic: TOleGraphic;
LocalHeader: TZipHeader;
begin
LZipFile := TZipFile.Create;
try
//open the compressed file
LZipFile.Open('C:\Users\Dexter\Desktop\registry.zip', zmRead);
//create the memory stream
LStream := TMemoryStream.Create;
try
//LZipFile.Read(0, LStream, LocalHeader); you can use the index of the file
LZipFile.Read('SAM_0408.JPG', LStream, LocalHeader); //or use the filename
//do something with the memory stream
//now using the TOleGraphic to detect the image type from the stream
LOleGraphic := TOleGraphic.Create;
try
LStream.Position:=0;
//load the image from the memory stream
LOleGraphic.LoadFromStream(LStream);
//load the image into the TImage component
Image1.Picture.Assign(LOleGraphic);
finally
LOleGraphic.Free;
end;
finally
LStream.Free;
end;
finally
LZipFile.Free;
end;
end;

How to save jpg image to database and then load it in Delphi using FIBplus and TImage?

How do I save a jpg image to database and then load it in Delphi using FIBplus and TImage?
var
S : TMemoryStream;
begin
S := TMemoryStream.Create;
try
TBlobField(AdoQuery1.FieldByName('ImageField')).SaveToStream(S);
S.Position := 0;
Image1.Picture.Graphic.LoadFromStream(S);
finally
S.Free;
end;
end;
if you are using JPEG images, add JPG unit to uses clause of your unit file.
This page explains it. Use SaveToStream and a TMemoryStream instead of SaveToFile if you don't want temporary files. TImage.Picture has a LoadFromStream which loads the image from the stream into the TImage for display.
Take a look here.
I think you have to convert it to a stream, store it and vice versa.
Delphi 7 paradox table
insert dbimage to jpeg
var
FileStream: TFileStream;
BlobStream: TStream;
begin
if openpicturedialog1.Execute then
begin
Sicil_frm.DBNavigator1.BtnClick(nbEdit);
image1.Picture.LoadFromFile(openpicturedialog1.FileName);
try
BlobStream := dm.sicil.CreateBlobStream(dm.sicil.FieldByName('Resim'),bmWrite);
FileStream := TFileStream.Create(openpicturedialog1.FileName,fmOpenRead or fmShareDenyNone);
BlobStream.CopyFrom(FileStream,FileStream.Size);
FileStream.Free;
BlobStream.Free;
Sicil_frm.DBNavigator1.BtnClick(nbPost);
DM.SicilAfterScroll(dm.sicil);
except
dm.sicil.Cancel;
end;
end;
end;
Error "Bitmap image is nat valid"

Resources