Download form - which library to use? lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- mesiarm · 2013-06-27 14:28 Hi. I want to make some download form with progress bar something like VLC player has  - when you click to download update. How to make it and which library (Synapse, Indy etc.) to use? I have Synapse,but i´m not sure if there is something to use progress bar and download big files (I want to download around 30 MB file). ---------------------------------------------------------------------- Rails · 2013-06-27 15:33 Use TProgressBar in ComCtrls. ---------------------------------------------------------------------- Leledumbo · 2013-06-27 15:43 All available networking libraries (fcl-net, synapse, lnet, indy) are AFAIK capable of doing this. Synapse: http://synapse.ararat.cz/doku.php/public:howto:ftp_progressbar (applicable to THTTPSend, only change from DSock to Sock to get the socket instance). ---------------------------------------------------------------------- mesiarm · 2013-06-28 20:05 thanks.... i tried it i made this program ...I don´t know.. how to continue.... i looked to this http://www.ararat.cz/synapse/doku.php/public:howto:onstatus i tried Code: [Select]http.Sock.OnStatus := SockPutCallBack(Sender: TObject,HR_ReadCount, value) I´m not sure with parameters and I don´t know which value to use Can you explain me a bit working of this program  please? Code: [Select]unit Unit1; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,   StdCtrls, blcksock, synafpc, synsock, synautil, synacode, synaip, httpsend; type   { TForm1 }   TForm1 = class(TForm)     httpclick: TButton;     pr: TProgressBar;     procedure httpclickClick(Sender: TObject);   private     { private declarations }   public     procedure SockPutCallBack(Sender: TObject;   Reason: THookSocketReason; const Value: string);     { public declarations }   end; var   Form1: TForm1;   CurrentBytes,TotalBytes : integer;   http : THttpsend; implementation {$R *.lfm} procedure TForm1.SockPutCallBack(Sender: TObject;   Reason: THookSocketReason; const Value: string); begin   case Reason of     HR_WriteCount: // HR_ReadCount // Value contains number of bytes       begin         inc(CurrentBytes, StrToIntDef(Value, 0)); // Increment uploaded bytes         pr.Position := Round(1000 * (CurrentBytes / TotalBytes));       end;     HR_Connect: CurrentBytes := 0; // Reset     (*       else         begin           v := getEnumName(typeInfo(THookSocketReason),             integer(Reason)) + ' ' + Value);           MemoLog.Lines.Add(v); // Show log in a Memo field         end;     *)   end; end; procedure TForm1.httpclickClick(Sender: TObject); begin   http := THTTPSend.Create;     try       http.Sock.OnStatus := SockPutCallBack;       finally         http.free;       end; end; end. ---------------------------------------------------------------------- Jurassic Pork · 2013-06-29 00:18 hello, 1 - you must get the size of the file which you want to download before downloading, to have the TotalBytes value. 2 - The downloading procedure need to be in a thread else your Form will freeze while download. here is an example  ( not sure, must be checked) : Code: [Select]unit Unit1; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics,   Dialogs, StdCtrls, ComCtrls, httpsend,blcksock,typinfo, synamisc; type    { THTTPDownload }   THTTPDownload = class(TThread)     private       http : THTTPsend;       TotalBytes    :  integer;       CurrentBytes  :  integer;       fURL: string;       fFileName: string;     procedure SocketGetCallBack(Sender: TObject; Reason: THookSocketReason;     const Value: string);     public       constructor Create(URL, FileName: string);       procedure Execute; override;   end;   { TForm1 }   TForm1 = class(TForm)     Button1: TButton;     ELink: TEdit;     ProgressBar1: TProgressBar;     procedure Button1Click(Sender: TObject);   private     { private declarations }   public     { public declarations }   end; var   Form1: TForm1; implementation {$R *.lfm} { THTTPDownload } constructor THTTPDownload.Create(URL, FileName: string); begin   inherited Create(True);   fURL:= URL;   fFileName:= FileName; end; procedure THTTPDownload.SocketGetCallBack(Sender: TObject; Reason: THookSocketReason;   const Value: string); begin   if TotalBytes=0 then     exit;   case Reason of     HR_ReadCount:     begin       Inc(CurrentBytes, StrToIntDef(Value, 0));       Form1.ProgressBar1.Position := Round(100 * (CurrentBytes / TotalBytes));     end;     HR_Connect: CurrentBytes := 0;   end; end; procedure THTTPDownload.Execute; const   MaxRetries=3; var   HTTPGetResult: boolean;   RetryAttempt: integer;   i:integer;   s:string;   httpresult : boolean; begin   httpresult:=false;   RetryAttempt:=1;   http :=THTTPSend.Create;   http.Sock.OnStatus := @SocketGetCallBack;   try     try       // try to get the file size       if http.HTTPMethod('HEAD', fURL) then          begin        // search for "Content-Length:" in header             for i:=0 to http.Headers.Count-1 do             begin              s:=UpperCase(http.Headers[i]);               if pos('CONTENT-LENGTH:',s)>0 then               TotalBytes := StrToIntDef(copy(s,pos(':',s)+1,length(s)),0);               end;           http.Headers.Clear;          end;       // Try to get the file       HTTPGetResult:=http.HTTPMethod('GET', fURL);       while (HTTPGetResult=false) and (RetryAttemptThe bold part is the real URL that directly gets the file. Scan that meta tag (manual parsing should be enough, full html parsing could be slow). QuoteAnd is there chance to even download latest version? maybe download from folder http://get.videolan.org:81/vlc/last/win32/You know the URL, so just do it. Many repo provide a "symlink" to the latest version, without specific version in the filename, but VLC doesn't seem to follow that so you still have to scan the html. Assuming the filename format doesn't change, you could just look for .exe suffix (there's only one file). Quoteand else .... how can I extract domain from url? I tried more examples with basic pos function  but i didn´t workedhttp://www.freepascal.org/docs-html/fcl/uriparser/index.html ---------------------------------------------------------------------- mesiarm · 2013-07-07 18:47 Thank you for advices. I´ll try it, but how to access http directory list, since there is no index.html file? ...and I have one other problem... i have main form which opens updater form as modal(Showmodal) and when i click on button in updater form it start downloading...and in thread in code above I have problem communicating with updater form and it´s progressbar. It raises exception while accesing to progressbar and when I use assigned(updaterform) in button onclick it returns false. It worked when I had it as separate form in test project, but stopped working, when I added it to my project. I dont know, where can be I mistake.... ---------------------------------------------------------------------- Jurassic Pork · 2013-07-08 03:01 hello, with this code it's OK for me (unit2 contains Updater form modal with a progressbar) : Code: [Select]unit Unit2; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,   httpsend,blcksock,typinfo, synamisc; type     { THTTPDownload }   THTTPDownload = class(TThread)     private       http : THTTPsend;       TotalBytes    :  integer;       CurrentBytes  :  integer;       fURL: string;       fFileName: string;     procedure SocketGetCallBack(Sender: TObject; Reason: THookSocketReason;     const Value: string);      procedure downloadterminated(Sender: TObject);     public       constructor Create(URL, FileName: string);       procedure Execute; override;   end;   { TUpdater }   TUpdater = class(TForm)     ProgressBar1: TProgressBar;   private     { private declarations }   public     { public declarations }   end; var   Updater: TUpdater; implementation {$R *.lfm} { THTTPDownload } constructor THTTPDownload.Create(URL, FileName: string); begin   inherited Create(True);   fURL:= URL;   fFileName:= FileName;   OnTerminate := @downloadterminated; end; procedure THTTPDownload.downloadterminated(Sender: TObject);  begin  Updater.Close;  end; procedure THTTPDownload.SocketGetCallBack(Sender: TObject; Reason: THookSocketReason;   const Value: string); begin   if TotalBytes=0 then     exit;   case Reason of     HR_ReadCount:     begin       Inc(CurrentBytes, StrToIntDef(Value, 0));       Updater.ProgressBar1.Position := Round(100 * (CurrentBytes / TotalBytes));     end;     HR_Connect: CurrentBytes := 0;   end; end; procedure THTTPDownload.Execute; const   MaxRetries=3; var   HTTPGetResult: boolean;   RetryAttempt: integer;   i:integer;   s,Redirstr:string;   httpresult : boolean;   TextHttp : TStringList; begin   httpresult:=false;   RetryAttempt:=1;   Texthttp := TStringList.Create();   http :=THTTPSend.Create;   http.Sock.OnStatus := @SocketGetCallBack;   try     try       // try to get the file size       if http.HTTPMethod('HEAD', fURL) then          begin        // search for "Content-Length:" in header             for i:=0 to http.Headers.Count-1 do             begin              s:=UpperCase(http.Headers[i]);               if pos('CONTENT-LENGTH:',s)>0 then               TotalBytes := StrToIntDef(copy(s,pos(':',s)+1,length(s)),0);               end;           http.Headers.Clear;          end;       // Try to get the file       HTTPGetResult:=http.HTTPMethod('GET', fURL);       while (HTTPGetResult=false) and (RetryAttempt vlc-2.0.7-win32.7z   re.Expression := '
  • (.*).+';   For x := 0 to httpText.Count -1  do   begin   s := httpText.Strings[x];   if re.Exec(s) then   ShowMessage('href : ' + re.Match[1] + ' - value : ' + re.Match[2]);   end;   re.Free; end; called from this line : Code: [Select]ListHTTPFiles(Texthttp); friendly, J.P ---------------------------------------------------------------------- mesiarm · 2013-07-09 18:12 I think it is a bit complicated with my project so I will post a image Code: [Select]http://img199.imageshack.us/img199/5716/ddyb.png ---------------------------------------------------------------------- mesiarm · 2013-07-12 14:28 i tried your procedure and it works. I have changed it to function and added .exe suffix to regural expression and it works, but i don´t know when call this function, because if I call it at beggining http.document is empty and later it points to url to directory. Should I make second connection or? ---------------------------------------------------------------------- Jurassic Pork · 2013-07-12 14:58 hello, here is  the code  for   procedure      THTTPDownload.Execute;  ( the game : find where is  ListHTTPFiles function  ) Code: [Select]procedure THTTPDownload.Execute; const   MaxRetries=3; var   HTTPGetResult: boolean;   RetryAttempt: integer;   i:integer;   s,Redirstr:string;   httpresult : boolean;   TextHttp : TStringList; begin   httpresult:=false;   RetryAttempt:=1;   Texthttp := TStringList.Create();   http :=THTTPSend.Create;   http.Sock.OnStatus := @SocketGetCallBack;   try     try       // try to get the file size       if http.HTTPMethod('HEAD', fURL) then          begin        // search for "Content-Length:" in header             for i:=0 to http.Headers.Count-1 do             begin              s:=UpperCase(http.Headers[i]);               if pos('CONTENT-LENGTH:',s)>0 then               TotalBytes := StrToIntDef(copy(s,pos(':',s)+1,length(s)),0);               end;           http.Headers.Clear;          end;       // Try to get the file       HTTPGetResult:=http.HTTPMethod('GET', fURL);       while (HTTPGetResult=false) and (RetryAttempt '' then   begin   URLtoDL := GetTextFromURL('http://get.videolan.org:81/vlc/last/win32/' +              FileNameToDL,'

    The document has moved '' then     begin      Updater.Progressbar1.Position := 0;      download:= THTTPDownload.Create(URLtoDL, FileNameToDL);      download.Resume;      Updater.ShowModal;      download.free;     end;   end; end; if you want more simple, use the great benibela's internet tools   (GPL) here : http://benibela.de/sources_en.html#internettools Friendly, J.P ---------------------------------------------------------------------- mesiarm · 2013-07-14 09:37 Jurrasic Pork thank you very much for your patience with me and for your advices  and  BeniBela also thank you  And please look at this image - it ´s preview of my project -  http://img199.imageshack.us/img199/5716/ddyb.png Acessing to form .... in main form updater is global variable ... and I have also changed Owner from nil to Application and Showmodal to Show and deleted updater.Free line  but it didnt worked. Now I have added line : Code: [Select]if assigned (self) then showmessage ('yes in form activate') else showmessage ('no in form activate');    to Tupdater.Formactivate and it returns yes... so in formactivate and other form methods it works because I have changed updater to self.... But in thread I have to call it as updater because thread is other class and there it returns no (if I call Code: [Select]if assigned (updater) then showmessage ('yes in thread') else showmessage ('no in thread'); and even Code: [Select]  if fupdater = nil then showmessage ('yes nil') else showmessage ('no nil');  returns yes nil in thread) Please help me ... ---------------------------------------------------------------------- Jurassic Pork · 2013-07-14 15:11 hello mesiarm, the best solution would be to put your whole project in attachment (if it is possible) to see what is wrong. Friendly, J.P ---------------------------------------------------------------------- mesiarm · 2013-07-28 12:56 Sorry I wasn´t online for long time but now I have created demo project of my problem because I wanted to know where s the problem - here it is : http://uploadir.com/uploads/l79yb6ji/downloads/new ---------------------------------------------------------------------- Jurassic Pork · 2013-07-28 15:46 Hello Mesiarm, i don't know why you don't work with Form2 all the time and why you use fupdater variable. With those changes it seems that your project run : 1 - remove unit2 from the uses at the beginning of the unit1. Code: [Select]interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls; 2 - replace your code by this code after  implementation : Code: [Select]implementation uses Unit2;  //  here is the trick {$R *.lfm} { TForm1 } procedure TForm1.btnClick(Sender: TObject); begin   Form2.showmodal;   end; end. 3 - in the unit2 replace all the occurences of Fupdater by Form2. Friendly, J.P ---------------------------------------------------------------------- mesiarm · 2013-07-30 18:48 Yes than you... now it works.... and please can you explain me what´s difference in using in interface and implementation part? Idon´t understand that.... ---------------------------------------------------------------------- Jurassic Pork · 2013-07-30 22:54 for example read this about circular unit references ---------------------------------------------------------------------- mesiarm · 2013-08-01 21:44 ok thanks   I have read it. now I want to implement cancel button and I have downloadthread.terminate command in cancelonclick, but it won´t cancel download... I have read that terminate only send instruction to terminate and i didn´t found way to force terminate thread ...so i wanted to add while not terminated loop.... debbugging I have found that during download is still running this command:  Code: [Select]HTTPGetResult:=http.HTTPMethod('GET', fURL); so i have added Code: [Select]while not terminated do HTTPGetResult:=http.HTTPMethod('GET', fURL); It didn´t worked... I wanted to use something like Application.ProcessMessages and since I can´t use this command inside thread I tried this code which i had found Code: [Select]  if MsgWaitForMultipleObjects(0, nil, FALSE, 10, QS_ALLINPUT) = WAIT_OBJECT_0 then     begin       while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do       begin         TranslateMessage(Msg);         DispatchMessage(Msg);       end;     end;    without success. What can I do with it? ---------------------------------------------------------------------- mesiarm · 2013-09-19 20:45 Hm I found it, with http.Abort.... but Jurrasic Pork you have earlier gave me function GetTextFromURL and it worked till now...please look at it...  now I don´t know, why it stopped working and although it with redirect with code you have said to me, when it connect to server, it will return 302 Moved Temporarily