I have components:
1 OpenPictureDialog (to open picture)
2 Edit (edtID & edtName)
1 Button (to save record)
I used UIBQuery to insert a new record, including picture in one section.
Here is my code:
with UIBQuery1 do
try
SQL.Clear;
SQL.Add('INSERT INTO EMPLOYEE');
SQL.Add('(ID, NAME, PIC)');
SQL.Add('VALUES');
SQL.Add('(:ID, :NAME, :PIC)');
params.AsInteger[0] := StrToInt(edtID.Text);
params.AsString[1] := edtName.Text;
// How to give a param for blob here?
Execute;
Transaction.Commit;
bsSkinMessage1.MessageDlg2('Has been saved.','New Record',mtInformation,[mbok],0);
except
Transaction.RollBack;
raise;
end;
I succeed show the record including picture from database, but I have no clue to store the picture into database in one click.
How to doing this? Is it possible to give parameter of picture in query?
Someone knows about UIB please..
I change my code but I get an access violation:
procedure TchfEmployee.btnSaveClick(Sender: TObject);
var
ms: TMemoryStream;
begin
ms := TMemoryStream.Create;
Image2.Picture.Bitmap.SaveToStream(ms);
ms.Position:=0;
begin
with UIBQuery1 do
try
SQL.Clear;
SQL.Add('INSERT INTO EMPLOYEE');
SQL.Add('(ID, NAME, PIC)');
SQL.Add('VALUES');
SQL.Add('(:ID, :NAME, :PIC)');
params.AsString[0] := edtID.Text;
params.AsString[1] := edtName.Text;
TBLOBField(Params.ByNameAsString['PIC']).LoadFromStream(ms);
Execute;
Transaction.Commit;
bsSkinMessage1.MessageDlg2('Has been saved.','New Record',mtInformation,[mbok],0);
except
Transaction.RollBack;
raise;
end;
try
UIBDataSet1.Close;
UIBDataSet1.Open;
except
raise;
end;
end;
end;
How's exactly save my TImage to BLOB using UIBQuery? I am really in a heavy stucked.
Try this:
...
ParamsSetBlob('PIC', ms);
Execute;
...
Take a look at UIB source code. Look for TUIBStatement class.
You need to do the following :-
Var
lStream : TMemoryStream;
Begin
lStream := TMemoryStream.Create;
Try
Image2.Picture.SaveToStream(lStream);
lStream.Position := 0;
Query.ParamSetBlob('Pic', lStream);
Finally
FreeAndNil(lStream);
End;
End;
Related
I would like to know how to save the contents of a "variable" after program is closed and reopened.
for eg:
iCount:=0;
inc(iCount)=1;
when i close the program and reopen i want iCount to contain 1.
Thank you.
There are many ways to do this. You need to save the value somewhere: in a file, in the Windows registry, in the cloud, ...
File
Perhaps the easiest approach is to use an INI file. Try this:
Create a new VCL application.
Add a field FMyNumber: Integer to the main form.
To the main form, add the following methods (and make sure to include IniFiles and IOUtils in the implementation section's uses list):
function TForm1.GetSettingsFileName: TFileName;
begin
Result := TPath.GetHomePath + '\Fuzail\TestApp';
ForceDirectories(Result);
Result := Result + '\settings.ini';
end;
procedure TForm1.LoadSettings;
var
Ini: TMemIniFile;
begin
Ini := TMemIniFile.Create(GetSettingsFileName);
try
FMyNumber := Ini.ReadInteger('Settings', 'MyNumber', 0);
finally
Ini.Free;
end;
end;
procedure TForm1.SaveSettings;
var
Ini: TMemIniFile;
begin
Ini := TMemIniFile.Create(GetSettingsFileName);
try
Ini.WriteInteger('Settings', 'MyNumber', FMyNumber);
Ini.UpdateFile;
finally
Ini.Free;
end;
end;
Now make sure to call these when your application is starting and shutting down:
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadSettings;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
SaveSettings;
end;
Now the value of FMyNumber is saved between the sessions!
Registry
Another common approach, probably, is to use the registry. Try this:
Create a new VCL application.
Add a field FMyNumber: Integer to the main form.
To the main form, add the following methods (and make sure to include Registry in the implementation section's uses list):
procedure TForm1.LoadSettings;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKey('\Software\Fuzail\TestApp', False) then
try
if Reg.ValueExists('MyNumber') then
FMyNumber := Reg.ReadInteger('MyNumber')
finally
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
procedure TForm1.SaveSettings;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKey('\Software\Fuzail\TestApp', True) then
try
Reg.WriteInteger('MyNumber', FMyNumber);
finally
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
Now make sure to call these when your application is starting and shutting down:
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadSettings;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
SaveSettings;
end;
Again the value of FMyNumber is saved between the sessions!
Please create a new FMX application, add a button and a memo to run this example. I have this code:
procedure TForm1.Button1Click(Sender: TObject);
begin
TTask.Run(procedure
var
client: TIdHTTP;
result: string;
begin
client := TIdHTTP.Create(nil);
try
try
client.ReadTimeout := 4000;
client.ConnectTimeout := 4000;
result := client.Get('a valid url here just as test');
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(result);
end);
except
on E: Exception do
begin
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(E.Message);
end);
end
end;
finally
client.Free;
end;
end);
end;
It works as I expect but the problem is in the IDE. If I place the cursor somewhere in the body of the anonymous function, I get the closing of the finally statement automatically.
How can I fix this?
First I am here
Then I press enter and I have this!
If you put the cursor at the beginning and not at the end of the line, you can add new spaces without the completion. How to solve this problem? Well, I have discovered that the issue happens because there is this code:
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(result);
end);
If you remove this code, the issue doens't happen anymore. Is this a bug in the IDE?
Is this a bug in the IDE?
Yes. This is a defect. Please submit a report to Quality Portal.
Is this a bug in the IDE?
Yes, this is a bug in the IDE. Your code is syntactically valid.
How can I fix this?
The best way to avoid this is to create your code and surround it with try...except... to handle any exception:
try
MyClass := TComponent.Create(Self);
try
finally
MyClass.Free;
end;
except on E: Exception do
end;
So your code will be:
TTask.Run(procedure
var
client: TIdHTTP;
result: string;
begin
try
Client := TIdHTTP.Create(nil);
try
client.ReadTimeout := 4000;
client.ConnectTimeout := 4000;
result := client.Get('a valid url here just as test');
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(result);
end);
finally
Client.Free;
end;
except on E: Exception do
begin
TThread.Synchronize(nil, procedure
begin
Memo1.Lines.Add(E.Message);
end);
end;
end;
end;
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;
I would like to use SaveToStream to save a ClientDataSet ALONG WITH OTHER MATERIAL. Here is a short sample:
filename := ChangeFileExt(Application.ExeName, '.dat');
FS := TFileStream.Create(filename, fmCreate);
CDS.SaveToStream(FS);
ShowMessage('After save, position is ' + IntToStr(FS.Position));
{now write a longint}
L := 1234;
siz := SizeOf(L);
Write(L, siz);
FS.Free;
But when I try to load this back in using LoadFromStream, and I again display the position after the ClientDataSet has been loaded, I see that the position is now 4 bytes AFTER the clientdataset was originally saved. It seems that CDS.LoadFromStream just plows ahead and consumes whatever follows it. As a result, when I then try to read the longint, I get an end of file error.
It is not sufficient to just use the CDS.SaveToStream at the end of creating a file, because what I'd really like to do is to save TWO clientdatasets to the file, one after the other, plus other material.
Ideas? Thanks.
[NB, this solution is essentially doubling up the work that (TLama's suggestion) "ReadDataPacket/WriteDataPacket" already does internally. I would use TLama's approach i.e. sub-class TClientDataSet to expose the above protected methods, and use the WriteSize parameter.]
Save the datasets to a temporary stream and then copy that to your destination stream with size information:
procedure InternalSaveToStream(AStream: TStream);
var
ATempStream: TMemoryStream;
ASize: Int64;
begin
ATempStream := TMemoryStream.Create;
// Save first dataset:
DataSet1.SaveToStream(ATempStream, dfBinary);
ASize := ATempStream.Size;
AStream.WriteData(ASize);
ATempStream.Position := 0;
AStream.CopyFrom(ATempStream, ALength);
ATempStream.Clear;
// Save second dataset:
DataSet2.SaveToStream(ATempStream, dfBinary);
ASize := ATempStream.Size;
AStream.WriteData(ASize);
ATempStream.Position := 0;
AStream.CopyFrom(ATempStream, ALength);
ATempStream.Clear;
FreeAndNil(ATempStream);
end;
To read back, first read the size and then copy that section of your source to a temporary stream again and load your dataset from that:
procedure InternalLoadFromStream(AStream: TStream);
var
ATempStream: TMemoryStream;
ASize: Int64;
begin
ATempStream := TMemoryStream.Create;
// Load first datset:
AStream.Read(ASize,SizeOf(ASize));
ASize := ATempStream.Size;
ATempStream.CopyFrom(AStream,ASize);
ATempStream.Position := 0;
DataSet1.LoadFromStream(ATempStream);
//...etc.
end;
I have Midas project that uses a TDataSetProvider in one of RemoteDataModules in the Server
Currently I am making use of the following events
BeforeApplyUpdates - to create an Object
BeforeUpdateRecord - to use the object
AfterApplyUpdates - to destory the object
Question:
Will ‘ AfterApplyUpdates’ always be called even if the is an update error ?
If you look at the sourcecode:
function TCustomProvider.DoApplyUpdates(const Delta: OleVariant; MaxErrors: Integer;
out ErrorCount: Integer; var OwnerData: OleVariant): OleVariant;
begin
SetActiveUpdateException(nil);
try
try
if Assigned(FOnValidate) then
FOnValidate(Delta);
DoBeforeApplyUpdates(OwnerData);
Self.OwnerData := OwnerData;
try
Result := InternalApplyUpdates(Delta, MaxErrors, ErrorCount);
finally
OwnerData := Self.OwnerData;
Self.OwnerData := unassigned;
end;
except
on E: Exception do
begin
SetActiveUpdateException(E);
raise;
end;
end;
finally
try
DoAfterApplyUpdates(OwnerData);
finally
SetActiveUpdateException(nil);
end;
end;
end;
Yoy see that DoAfterApplyUpdates is called in the finally block. This means it is always called regardles of any exception.