How to display progress of the TFDScript execution using TProgressBar? - sqlite

I have some script in some file "MyScript.sql"
On the form I have my TProgressBar.
I want to read script with TFDScript and move progressbar according to the script.
My code is
Var
Lista: TStringList; // SQL DDL list for creating table and populate table
I: Integer;
Begin
With FDConn Do //FDConn is my FaireDac connection
Begin
LoginPrompt := False;
With Params Do
Begin
Clear;
DriverID := 'SQLite';
Database := 'MyDatabase.sdb';
LoginPrompt := False;
End;
Lista := TStringList.Create;
Lista.Clear;
Try
FDScript.ValidateAll; //FDScript is TFDScript and prgBar is TProgressBar
prgBar.Max := FDScript.TotalJobSize - 1;
prgBar.Update;
Lista.Clear;
Lista.LoadFromFile('MyScript.sql');
// Now how I can read script 1 line by 1 line and move progress bar with
prgBar.StepIt;
prgBar.Update;`

You can handle the OnProgress event and read there e.g. TotalJobSize property to determine the number of bytes to proceed and TotalJobDone to get number of bytes processed. For example:
procedure TForm1.FDScript1Progress(Sender: TObject);
begin
ProgressBar1.Max := TFDScript(Sender).TotalJobSize;
ProgressBar1.Position := TFDScript(Sender).TotalJobDone;
end;
If you were having progress bar control with progress value setup by percentage, you'd better read the TotalPct10Done property.

Related

IdHttpServer doesn't serve file

We have a problem calling websites assests from Indy TIdHTTPServer in Delphi. Some times, Indy can serve media files, some times not, but the problem is just when we are calling over the network. It is working very well when calling over localhost.
For example, if we call it over the network like https://192.168.1.113:5000/?page=index then Indy cannot serve some files, if we call like https://localhost:5000/?page=index it is working well.
procedure TFolsecPermissionManager.IdHTTPServerCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Command: TCommand;
sFileName: string;
begin
// IdHTTPServer.CreateSession(AContext, AResponseInfo, ARequestInfo);
AContext.Connection.Socket.ReadTimeout := 6000000;
if CommandRequestFileType(ARequestInfo.URI) = 'MEDIA' then
begin
//AResponseInfo.ContentStream := CommandGetMedia(ARequestInfo.URI);
sFileName := CommandGetMedia2(ARequestInfo.URI);
if sFileName <> '' then
begin
AResponseInfo.ContentType := GetContentType(ARequestInfo.URI);
AResponseInfo.ServeFile(AContext, sFileName); // --- here doesn't work to serve file
end else
AResponseInfo.WriteContent;
exit;
end else begin
Command:= TCommand.Create;
try
Command.AContext := AContext;
Command.ARequestInfo := ARequestInfo;
Command.AResponseInfo := AResponseInfo;
Command.Synchronize;
finally
Command.Free;
end;
end;
end;

Delphi: Delete files in a directory older than X days and/or having a special file mask (*.xxx)

Language: Delphi 10.1 Berlin
Problem:
There is a directory with measurement files (*.csv) and other files.
Every few hours a new measurement file will be created.
I need a possibility to delete all .csv files in that folder that are older than a specific number of days. All other file types should not be touched.
Question:
Is there any built-in function in Delphi to do that job? If not, what is an efficient way to solve this problem?
I didn't find a Delphi built-in function for that specific problem.
This function worked for me:
function TUtilities.DeleteFilesOlderThanXDays(
Path: string;
DaysOld: integer = 0; // 0 => Delete every file, ignoring the file age
FileMask: string = '*.*'): integer;
var
iFindResult : integer;
SearchRecord : tSearchRec;
iFilesDeleted: integer;
begin
iFilesDeleted := 0;
iFindResult := FindFirst(TPath.Combine(Path, FileMask), faAnyFile, SearchRecord);
if iFindResult = 0 then begin
while iFindResult = 0 do begin
if ((SearchRecord.Attr and faDirectory) = 0) then begin
if (FileDateToDateTime(SearchRecord.Time) < Now - DaysOld) or (DaysOld = 0) then begin
DeleteFile(TPath.Combine(Path, SearchRecord.Name));
iFilesDeleted := iFilesDeleted + 1;
end;
end;
iFindResult := FindNext(SearchRecord);
end;
FindClose(SearchRecord);
end;
Result := iFilesDeleted;
end;
procedure DeleteFilesOlderThan(
const Days: Integer;
const Path: string;
const SearchPattern: string = '*.*');
var
FileName: string;
OlderThan: TDateTime;
begin
Assert(Days >= 0);
OlderThan := Now() - Days;
for FileName in TDirectory.GetFiles(Path, SearchPattern) do
if TFile.GetCreationTime(FileName) < OlderThan then
TFile.Delete(FileName);
end;

Create video streaming server in indy

I am trying to create video streaming server using Indy Http server. I am using ranged requests to send large files. One chunk of data is 10 Mb long. If video file which requests client is smaller than 10 Mb then it is all ok and vido is played. But if file size is longer than 10 Mb I return first chunk of data. Then client asks me for another chunk of data from the end of file and then my client says that it is unrecognizable video format. Can someone tell me where is problem in my code.
my server code
procedure TForm1.Button1Click(Sender: TObject);
begin
Caption := 'Running';
FServer := TIdHTTPServer.Create(Self);
FServer.DefaultPort := 7070;
FServer.OnCommandGet:=#External_Get;
FServer.Active := True;
end;
procedure TForm1.External_Get(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
FS: TFileStream;
Ranges: TIdEntityRanges;
Range: TIdEntityRange;
begin
Ranges := ARequestInfo.Ranges;
Range := Ranges.Ranges[0];
FS := TFileStream.Create('/home/user/Desktop/large_file.mp4', fmOpenRead or fmShareDenyWrite);
AResponseInfo.ContentType := 'video/mp4';
AResponseInfo.AcceptRanges := 'bytes';
AResponseInfo.ContentStream := TIdHTTPRangeStream.Create(
FS,
Range.StartPos,
Range.StartPos + 1024*1024*10,
True
);
AResponseInfo.FreeContentStream := True;
AResponseInfo.ContentRangeStart := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeStart;
AResponseInfo.ContentRangeEnd := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeEnd;
AResponseInfo.ContentRangeInstanceLength := AResponseInfo.ContentRangeEnd - Range.StartPos + 1;
AResponseInfo.ContentLength := FS.Size;
AResponseInfo.ResponseNo := 206;
end;
And here is my client code (I use firefox):
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body>
<video width="400" controls>
<source src="http://localhost:7070/test38.mp4" type="video/mp4">
Your browser does not support HTML5 video.
</video>
</body>
</html>
There are several errors in your server code.
You are not validating that a range is actually being requested, or even respecting an end range if one is present.
You are setting the AResponseInfo.ContentLength property to the full size of the file, even when you are not sending the full file at one time. That value belongs in the AResponseInfo.ContentRangeInstanceLength property instead when sending a ranged response. You must set ContentLength to the size of the data actually being sent in the response, which in this case is your current range chunk. It is best not to set the ContentLength at all, you can let the server calculate it for you based on the assigned ContentStream.
You are setting the AResponseInfo.ResponseNo property to 206 unconditionally, even if a range is not requested at all, or if the requested range cannot be satisfied. TIdHTTPRangeStream performs validations in its constructor and sets its ResponseCode property accordingly. That is the value you should be assigning to ResponseNo.
Try something more like this instead:
procedure TForm1.External_Get(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
FS: TFileStream;
Range: TIdEntityRange;
StartPos, EndPos: Int64;
begin
if not FileExists('/home/user/Desktop/large_file.mp4') then
begin
AResponseInfo.ResponseNo := 404;
Exit;
end;
try
FS := TFileStream.Create('/home/user/Desktop/large_file.mp4', fmOpenRead or fmShareDenyWrite);
except
AResponseInfo.ResponseNo := 500;
Exit;
end;
AResponseInfo.ContentType := 'video/mp4';
AResponseInfo.AcceptRanges := 'bytes';
if ARequestInfo.Ranges.Count = 1 then
begin
Range := ARequestInfo.Ranges.Ranges[0];
StartPos := Range.StartPos;
EndPos := Range.EndPos;
if StartPos >= 0 then
begin
// requesting prefix range from BOF
if EndPos >= 0 then
EndPos := IndyMin(EndPos, StartPos + (1024*1024*10) - 1)
else
EndPos := StartPos + (1024*1024*10) - 1;
end else
begin
// requesting suffix range from EOF
if EndPos >= 0 then
EndPos := IndyMin(EndPos, 1024*1024*10)
else
EndPos := (1024*1024*10);
end;
AResponseInfo.ContentStream := TIdHTTPRangeStream.Create(FS, StartPos, EndPos);
AResponseInfo.ResponseNo := TIdHTTPRangeStream(AResponseInfo.ContentStream).ResponseCode;
if AResponseInfo.ResponseNo = 206 then
begin
AResponseInfo.ContentRangeStart := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeStart;
AResponseInfo.ContentRangeEnd := TIdHTTPRangeStream(AResponseInfo.ContentStream).RangeEnd;
AResponseInfo.ContentRangeInstanceLength := FS.Size;
end;
end else
begin
AResponseInfo.ContentStream := FS;
AResponseInfo.ResponseNo := 200;
end;
end;

Idhttp + Download + Delphi + ASP.NET

I need to program an application with Delphi that goes into this site and uses the form to get an .exe file (in fact, the site sends a .ex_ file that you have to manually rename).
http://www.bmf.com.br/arquivos1/arquivos_ipn.asp?idioma=pt-BR&status=ativo
Via browser I just click on the checkbox on the left of "CenĂ¡rios de Margem - CORE" then click on the Download button and get the file automatically.
I managed to work with .dat files from other site, now I don't know what might be wrong.
I think the problem should be with the content type or how i'm saving the file.
This is what I got so far:
procedure DownloadViaPost;
var
objHttp: TIdHttp;
sUrl: String;
sGetRequest: String;
objParametrosPost: TStringList;
objRespostaPost: TStringStream;
sViewState: String;
sEventValidation: String;
begin
sUrl := 'http://www.bmf.com.br/arquivos1/arquivos_ipn.asp';
objHttp := TIdHTTP.Create(nil);
objParametrosPost := TStringList.Create;
objRespostaPost := TStringStream.Create;
try
objHttp.HandleRedirects := true;
objHttp.AllowCookies := true;
objParametrosPost.Add('hdnStatus=ativo');
objParametrosPost.Add('chkArquivoDownload_ativo=36');
objParametrosPost.Add('txtDataDownload_ativo=21/08/2014');
objParametrosPost.Add('imgSubmeter.x=31');
objParametrosPost.Add('imgSubmeter.y=9');
objParametrosPost.Add('imgSubmeter=ativo');
objHttp.Request.ContentType := 'application/octet-stream exe';
objHttp.Post(sUrl, objParametrosPost, objRespostaPost);
objRespostaPost.SaveToFile('C:\Download.ex_');
finally
FreeAndNil(objHttp);
FreeAndNil(objParametrosPost);
FreeAndNil(objRespostaPost);
end;
end;
Just like a browser would, you need to first retreive the download page to get the server's cookies, then post the download request so the cookies can be sent back to the server.
Try this:
procedure DownloadViaPost;
var
objHttp: TIdHttp;
objRespostaPost: TMemoryStream;
objParametrosPost: TStringList;
begin
objHttp := TIdHTTP.Create(nil);
try
objHttp.HandleRedirects := true;
objHttp.AllowCookies := true;
objHttp.Get('http://www.bmf.com.br/arquivos1/arquivos_ipn.asp?idioma=pt-BR&status=ativo');
objRespostaPost := TMemoryStream.Create;
try
objParametrosPost := TStringList.Create;
try
objParametrosPost.Add('hdnStatus=ativo');
objParametrosPost.Add('chkArquivoDownload_ativo=36');
objParametrosPost.Add('txtDataDownload_ativo=22/08/2014');
objParametrosPost.Add('imgSubmeter.x=37');
objParametrosPost.Add('imgSubmeter.y=6');
objHttp.Request.Referer := 'http://www.bmf.com.br/arquivos1/arquivos_ipn.asp?idioma=pt-BR&status=ativo';
objHttp.HTTPOptions := objHttp.HTTPOptions + [hoKeepOrigProtocol, hoTreat302Like303];
objHttp.Post('http://www.bmf.com.br/arquivos1/download_ipn.asp', objParametrosPost, objRespostaPost);
finally
FreeAndNil(objParametrosPost);
end;
objRespostaPost.SaveToFile('C:\Download.exe');
finally
FreeAndNil(objRespostaPost);
end;
finally
FreeAndNil(objHttp);
end;
end;

How to properly change a page's master page?

I have two master pages in my ASP.NET application. One for regular use, and another for printing. I use a session parameter to see if the application is currently in print mode or not:
method Global.Application_PreRequestHandlerExecute(src: System.Object; e: EventArgs);
begin
var p: System.Web.UI.Page := System.Web.UI.Page(self.Context.Handler);
if p <> nil then begin
p.PreInit += new EventHandler(page_PreInit)
end
end;
method Global.page_PreInit(sender: System.Object; e: EventArgs);
begin
var p: System.Web.UI.Page := System.Web.UI.Page(self.Context.Handler);
if p <> nil then
if p.Master <> nil then
begin
if Session['P'].ToString = '1' then
p.MasterPageFile := '~/Print.Master'
else
p.MasterPageFile := '~/Site.Master';
end;
end;
I have one button on my normal page which sets Session['P'] to '1', and another on my print master page which sets Session['P'] to '0'. Now, my problem is that after the I have changed the session parameter in my code, the page is rendered using the obsolete master page, and not the current one. The user has to hit F5 to see the correct page. It almost seems like my page_PreInit() event is fired before buttonClick(). So, what can I do?
Page_PreInit does run before any click event handlers.
Have you considered using panels or stylesheets to render your page in print mode?
I finally used Request.Params['__EVENTTARGET'] in my Page_PreInit event to determine if the clicked control is the button tasked with switching between normal and print modes. My code looks like this:
S := Request.Params['__EVENTTARGET'];
if S.Length > 0 then
S := S.Substring(S.IndexOf('$') + 1);
if S = 'lbPrint' then
Session['P'] := '1'
else if S = 'lbNormal' then
Session['P'] := '0';

Resources