Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: IdHTTP download from Dropbox


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 18 - Last Post: Mar 4, 2016 2:50 AM Last Post By: Jay Dee
Jay Dee

Posts: 44
Registered: 11/22/10
IdHTTP download from Dropbox  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 25, 2016 8:58 AM
Hi there everyone,

I'm trying to download a file from Dropbox using IdHTTP. The link I'm using is
http://dl.dropbox.com/u/12733424/Blog/Delphi%20Wmi%20Code%20Creator/Setup_WmiDelphiCodeCreator.exe

After lots of searching, I found a thread with an example in C++ Builder http://forums2.atozed.com/viewtopic.php?f=7&t=26905

I've tried to covert this to Delphi/FreePascal but I'm stuck on a line in IdHTTPHeadersAvailable of the example

if ((IdHTTP->Response->ContentStream) && (IdHTTP->Request->Ranges->Count > 0))
   IdHTTP->Response->ContentStream->Size = 0;


I converted it to Pascal but I get an error on HTTP.Response.ContentStream saying Error: Operator is not overloaded: "TStream" and "Boolean"

if ((HTTP.Response.ContentStream) and (HTTP.Request.Ranges.Count > 0)) then
   HTTP.Response.ContentStream.Size := 0;


How do I convert it so that I can test the example?

Thanks

JayDee

Edited by: Jay Dee on Feb 25, 2016 7:27 PM

Edited by: Jay Dee on Feb 25, 2016 7:29 PM
Dave Nottage

Posts: 1,850
Registered: 1/7/00
Re: IdHTTP download from Dropbox [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 25, 2016 11:09 AM   in response to: Jay Dee in response to: Jay Dee
Jay Dee wrote:

if ((HTTP.Response.ContentStream) and (HTTP.Request.Ranges.Count > 0)) then
   HTTP.Response.ContentStream.Size := 0;

That should read:

if ((HTTP.Response.ContentStream <> nil) and (HTTP.Request.Ranges.Count > 0)) then
   HTTP.Response.ContentStream.Size := 0;


--
Dave Nottage [TeamB]
Delphi Worlds blog: http://delphi.radsoft.com.au/blog
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 25, 2016 2:51 PM   in response to: Dave Nottage in response to: Dave Nottage
Thanks for your reply. I was able to compile & test the code. The testing brought up another exception saying "IOHandler value is not valid" on the line IdHTTP1.Head(strURL)

Here are the relevant procedures in my application for downloading from DropBox.

//
//    acDemarrer
//
procedure TfrmMain.acDemarrerExecute(Sender: TObject);
const
  SOURCE_FILE = 'http://dl.dropbox.com/u/12733424/Blog/Delphi%20Wmi%20Code%20Creator/Setup_WmiDelphiCodeCreator.exe';
var
  FS: TFileStream;
  strURL,
  strFileName,
  strDestinationFile: string;
begin
  //
  if SOURCE_FILE <> EmptyStr then
  begin
    //
    bcbtnDemarrer.Enabled := False;
    //
    strDestinationFile := GetCurrentDir() + PathDelim + 'Setup_WmiDelphiCodeCreator.exe';
    // Create IdHTTP object & set some properties
    IdHTTP1                     := TIdHTTP.Create(nil);
  	 IdHTTP1.ConnectTimeout      := 70000;
  	 IdHTTP1.ReadTimeout         := 70000;
    IdHTTP1.HandleRedirects     := True;
    IdHTTP1.OnWork              := @HttpWork;
    IdHTTP1.OnHeadersAvailable  := @HTTPHeadersAvailable;
    //
    strURL := SOURCE_FILE;
 
    //
    try
      //
      try
        // Get the name of the remote file to be downloaded
        //
        IdHTTP1.Head(strURL);                          // <----- Exception occurs here (IOHandler value is not valid)
        // Request first redirected URL
        strFileName := IdHTTP1.Response.RawHeaders.Params['Content-Disposition', 'filename'];
        //
        if (strFileName = EmptyStr) then
        begin
         // Request second redirected URL (if necessary)
         strFileName := IdHTTP1.Response.RawHeaders.Params['Content-Type', 'name'];
         //
         if (strFileName = EmptyStr) then
           strFileName := IdHTTP1.URL.Document;
        end;  // if (strFileName = EmptyStr) then
 
        // Filestream writes the file directly to disk
        // as opposed to a memory stream which first writes to memory
        FS      := TFileStream.Create(strDestinationFile, fmCreate);
        //
        try
          try
            // Download the file
            IdHTTP1.Get(Trim(SOURCE_FILE), FS);
          except
            on E: EIdHTTPProtocolException do begin
              // free the file handle before deleting the file
              if Assigned(FS) then FS.Free;
              // delete the file because the download failed
              if FileExists(strDestinationFile) then
                DeleteFile(strDestinationFile);
              // Display the error message
              MessageDlg(E.ErrorMessage, mtWarning, [mbOK], 0);
            end;
          end;
        finally
          // free the file handle before deleting the file
          if Assigned(FS) then FS.Free;
        end;      // try...finally
      except
         on E: EIdHTTPProtocolException do begin
           // Display the error message
           MessageDlg(E.ErrorMessage, mtWarning, [mbOK], 0);
         end;
      end;
    finally
      // free the HTTP component
      IdHTTP1.Free;
      //
      bcbtnDemarrer.Enabled := True;
    end;
  end;
end;
 
 
//
//    HttpWork
//
procedure TfrmMain.HttpWork(ASender: TObject;
  AWorkMode: TWorkMode; AWorkCount: Int64);
var
  Http: TIdHTTP;
  intNumBytes: int64;
begin
  //
  Http := TIdHTTP(ASender);
  //
	if (HTTP.Request.Method <> 'GET') then
		exit;
  //
	if ((AWorkMode = wmRead) and (HTTP.Response.ContentStream <> nil)) then
	begin
	  //if (AWorkCount >= (1024*1024*3))
	  //	throw Exception("simulated break"); // simulate a broken download every ~3MB
     //
	  intNumBytes := HTTP.Response.ContentStream.Position;
     //
	  if (ProgressBar1.Max > 0) then
	     ProgressBar1.Position := intNumBytes;
	end
end;
 
//
//    HTTPHeadersAvailable
//
procedure TfrmMain.HTTPHeadersAvailable(ASender: TObject;
  AHeaders: TIdHeaderList; var VContinue: boolean);
var
  Http: TIdHTTP;
begin
  //
  Http := TIdHTTP(ASender);
  //
	if (HTTP.Request.Method <> 'GET') then
		exit;
  //
	if ((HTTP.Response.ResponseCode = 200) or (HTTP.Response.ResponseCode = 206)) then
	begin
    //
    CanResume := (AnsiCompareText(AHeaders.Values['Accept-Ranges'], 'bytes') = 0);
 
    //
		ProgressBar1.Position := 0;
    //
		if (HTTP.Response.ResponseCode = 200) then
		begin
			if (HTTP.Response.HasContentLength) then
				ProgressBar1.Max := HTTP.Response.ContentLength
			else
				ProgressBar1.Max := 0;
      //
			if ((HTTP.Response.ContentStream <> nil) and (HTTP.Request.Ranges.Count > 0)) then
				HTTP.Response.ContentStream.Size := 0;
		end
		else if (HTTP.Response.ContentRangeInstanceLength > -1) then
		begin
			ProgressBar1.Max := HTTP.Response.ContentRangeInstanceLength;
			ProgressBar1.Position := HTTP.Response.ContentRangeStart;
 
			//Label.Caption = "Bytes recv: " + String(ProgressBar.Position);
		end
		else
		begin
			ProgressBar1.Max := 0;
		end;
	end;
end;


What am I missing? Thanks a lot for your kind assistance,

JayDee

Dave Nottage wrote:
Jay Dee wrote:

if ((HTTP.Response.ContentStream) and (HTTP.Request.Ranges.Count > 0)) then
   HTTP.Response.ContentStream.Size := 0;

That should read:

if ((HTTP.Response.ContentStream <> nil) and (HTTP.Request.Ranges.Count > 0)) then
   HTTP.Response.ContentStream.Size := 0;


--
Dave Nottage [TeamB]
Delphi Worlds blog: http://delphi.radsoft.com.au/blog

Edited by: Jay Dee on Feb 25, 2016 11:53 PM

Edited by: Jay Dee on Feb 25, 2016 11:54 PM

Edited by: Jay Dee on Feb 25, 2016 11:56 PM
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: IdHTTP download from Dropbox [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 25, 2016 3:40 PM   in response to: Jay Dee in response to: Jay Dee
Jay wrote:

The testing brought up another exception saying *"IOHandler value
is not valid"* on the line IdHTTP1.Head(strURL)

That error means an HTTPS url is being requested but an SSLIOHandler component,
such as TIdSSLIOHandlerSocketOpenSSL, is not assigned to the TIdHTTP.IOHandler
property. Without that, TIdHTTP cannot use SSL/TLS encryption.

If you are using an up-to-date version of Indy, it can create the SSLIOHandler
for you:

New HTTPS functionality for TIdHTTP
http://indyproject.org/Sockets/blogs/ChangeLog/20141222.aspx

If you are using an older version, you will have to assign the IOHandler
manually.

--
Remy Lebeau (TeamB)
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 25, 2016 10:09 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for your reply. That is great news indeed! SSL functionality out-of-the-box! What is the name of the unit I need to add to my uses clause? I added IdSSL to the uses clause but I still get the same exception as before.

Do I also need to have the actual OpenSSL dll files on my system or in the same directory as my program?

Thanks,

JayDee

Remy Lebeau (TeamB) wrote:
Jay wrote:

The testing brought up another exception saying *"IOHandler value
is not valid"* on the line IdHTTP1.Head(strURL)

That error means an HTTPS url is being requested but an SSLIOHandler component,
such as TIdSSLIOHandlerSocketOpenSSL, is not assigned to the TIdHTTP.IOHandler
property. Without that, TIdHTTP cannot use SSL/TLS encryption.

If you are using an up-to-date version of Indy, it can create the SSLIOHandler
for you:

New HTTPS functionality for TIdHTTP
http://indyproject.org/Sockets/blogs/ChangeLog/20141222.aspx

If you are using an older version, you will have to assign the IOHandler
manually.

--
Remy Lebeau (TeamB)

Edited by: Jay Dee on Feb 26, 2016 7:09 AM
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 1:44 AM   in response to: Jay Dee in response to: Jay Dee
I just downloaded the latest version of the OpenSSL dlls & I copied them to my application's directory. I ran the App & it still gives me the "IOHandler value is not valid" exception.

I guess I still haven't found the right OpenSSL unit to add to the uses clause. I only have IdSSL there at the moment.

Thanks,

JayDee
Antonio Estevez

Posts: 665
Registered: 4/12/00
Re: IdHTTP download from Dropbox [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 8:48 AM   in response to: Jay Dee in response to: Jay Dee
El 26/02/2016 a las 10:44, Jay Dee escribió:
I just downloaded the latest version of the OpenSSL dlls & I copied them to my application's directory. I ran the App & it still gives me the "IOHandler value is not valid" exception.

I guess I still haven't found the right OpenSSL unit to add to the uses clause. I only have IdSSL there at the moment.

Thanks,

JayDee

uses
...
   IdSSLOpenSSL,
...
 
     IdHTTP1                     := TIdHTTP.Create(nil);
     IdHTTP1.IOHandler           := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
     IdHTTP1.ConnectTimeout      := 70000;
...
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 12:27 PM   in response to: Antonio Estevez in response to: Antonio Estevez
Wonderful! It worked. Thanks a lot to everyone who took the time to help me solve this problem.

I did notice however that it worked with or without the SSL dlls in the project's directory. How does that work?

JayDee

Antonio Estevez wrote:
El 26/02/2016 a las 10:44, Jay Dee escribió:
I just downloaded the latest version of the OpenSSL dlls & I copied them to my application's directory. I ran the App & it still gives me the "IOHandler value is not valid" exception.

I guess I still haven't found the right OpenSSL unit to add to the uses clause. I only have IdSSL there at the moment.

Thanks,

JayDee

uses
...
   IdSSLOpenSSL,
...
 
     IdHTTP1                     := TIdHTTP.Create(nil);
     IdHTTP1.IOHandler           := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
     IdHTTP1.ConnectTimeout      := 70000;
...
Antonio Estevez

Posts: 665
Registered: 4/12/00
Re: IdHTTP download from Dropbox [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 12:41 PM   in response to: Jay Dee in response to: Jay Dee
El 26/02/2016 a las 21:27, Jay Dee escribió:
Wonderful! It worked. Thanks a lot to everyone who took the time to help me solve this problem.

I did notice however that it worked with or without the SSL dlls in the project's directory. How does that work?

Perhaps another application have installed them and added the folder where they are to the System PATH

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: IdHTTP download from Dropbox [Edit] [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 12:51 PM   in response to: Jay Dee in response to: Jay Dee
Jay wrote:

That is great news indeed! SSL functionality out-of-the-box!

Almost.

What is the name of the unit I need to add to my uses clause?

IdSSLOpenSSL

Do I also need to have the actual OpenSSL dll files on my system or in
the same directory as my program?

Yes. Unless you choose to use a different SSLIOHandler implementation (such
as Eldos SecureBlockbox) that is not based on OpenSSL.

--
Remy Lebeau (TeamB)
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit] [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 1:15 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
OK I'll keep the SSL dlls in the program directory just to be on the safe side. Can I force the program to "use" the dlls in its directory instead of those installed by another program? If so, how do I do it?

JayDee

Remy Lebeau (TeamB) wrote:
Jay wrote:

That is great news indeed! SSL functionality out-of-the-box!

Almost.

What is the name of the unit I need to add to my uses clause?

IdSSLOpenSSL

Do I also need to have the actual OpenSSL dll files on my system or in
the same directory as my program?

Yes. Unless you choose to use a different SSLIOHandler implementation (such
as Eldos SecureBlockbox) that is not based on OpenSSL.

--
Remy Lebeau (TeamB)
Antonio Estevez

Posts: 665
Registered: 4/12/00
Re: IdHTTP download from Dropbox  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 1:27 PM   in response to: Jay Dee in response to: Jay Dee
El 26/02/2016 a las 22:15, Jay Dee escribió:
OK I'll keep the SSL dlls in the program directory just to be on the safe side. Can I force the program to "use" the dlls in its directory instead of those installed by another program? If so, how do I do it?

You don't have to do anything.
The dlls in the same folder as executable have preference.

Unless the application calls the function SetDllDirectory sometime before the dlls are loaded:
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit] [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 1:43 PM   in response to: Antonio Estevez in response to: Antonio Estevez
Thanks a million Antonio.

JayDee

Antonio Estevez wrote:
El 26/02/2016 a las 22:15, Jay Dee escribió:
OK I'll keep the SSL dlls in the program directory just to be on the safe side. Can I force the program to "use" the dlls in its directory instead of those installed by another program? If so, how do I do it?

JayDee

Remy Lebeau (TeamB) wrote:
Jay wrote:

That is great news indeed! SSL functionality out-of-the-box!

Almost.

What is the name of the unit I need to add to my uses clause?

IdSSLOpenSSL

Do I also need to have the actual OpenSSL dll files on my system or in
the same directory as my program?

Yes. Unless you choose to use a different SSLIOHandler implementation (such
as Eldos SecureBlockbox) that is not based on OpenSSL.

--
Remy Lebeau (TeamB)

You don't have to do anything.
The dlls in the same folder as executable have preference.

Edited by: Jay Dee on Mar 4, 2016 11:48 AM
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: IdHTTP download from Dropbox [Edit] [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 26, 2016 3:00 PM   in response to: Jay Dee in response to: Jay Dee
Jay wrote:

Can I force the program to "use" the dlls in its directory instead
of those installed by another program? If so, how do I do it?

There is an IdOpenSSLSetLibPath() function available in the IdSSLOpenSSLHeaders
unit, eg:

uses
  ..., IdSSHOpenSSLHeaders;
 
procedure TMainForm.FormCreate(Sender: TObject);
begin
  IdOpenSSLSetLibPath(ExtractFilePath(Application.ExeName));
end;


--
Remy Lebeau (TeamB)
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit] [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 27, 2016 12:36 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for all your help. There is a line in the C++ builder code you posted that I don't understand.

if (GetQueueStatus(QS_ALLINPUT) != 0)
	Application->ProcessMessages();


What is if (GetQueueStatus(QS_ALLINPUT) != 0), how does it work with Application.ProcessMessages & finally how do I convert it to Delphi/FreePascal?

Thanks,

JayDee

Remy Lebeau (TeamB) wrote:
Jay wrote:

Can I force the program to "use" the dlls in its directory instead
of those installed by another program? If so, how do I do it?

There is an IdOpenSSLSetLibPath() function available in the IdSSLOpenSSLHeaders
unit, eg:

uses
  ..., IdSSHOpenSSLHeaders;
 
procedure TMainForm.FormCreate(Sender: TObject);
begin
  IdOpenSSLSetLibPath(ExtractFilePath(Application.ExeName));
end;


--
Remy Lebeau (TeamB)
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: IdHTTP download from Dropbox  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 29, 2016 12:12 PM   in response to: Jay Dee in response to: Jay Dee
Jay wrote:

What is if (GetQueueStatus(QS_ALLINPUT) != 0)

GetQueueStatus() is a Microsoft Win32 API function:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644940.aspx

how does it work with Application.ProcessMessages

The code is using GetQueueStatus() to check if the calling thread's message
queue has any pending messages that need to be processed. There is no point
in calling ProcessMessages() if there is nothing to process. And besides,
calling ProcessMessages() too often, especially in a loop, can cause performance
issues.

finally how do I convert it to Delphi/FreePascal?

Something like this:

unit Unit1;
 
interface
 
uses
  Classes, Controls, StdCtrls, Forms, Dialogs, IdHeaderList, ComCtrls, IdBaseComponent, 
IdComponent, IdHTTP, IdTCPClient, IdTCPConnection, IdSSLOpenSSL, IdIOHandler, 
IdIOHandlerSocket, IdIOHandlerStack, IdSSL;
 
type
  TForm1 = class(TForm)
    Download: TButton;
    IdHTTP: TIdHTTP;
    IdSSL: TIdSSLIOHandlerSocketOpenSSL;
    SaveDialog: TSaveDialog;
    Edit1: TEdit;
    ProgressBar: TProgressBar;
    Cancel: TButton;
    Resume: TButton;
    Label: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure DownloadClick(Sender: TObject);
    procedure IdHTTPHeadersAvailable(Sender: TObject; AHeaders: TIdHeaderList; 
var VContinue: Boolean);
    procedure IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: 
Int64);
    procedure CancelClick(Sender: TObject);
    procedure ResumeClick(Sender: TObject);
  private
    {User declarations}
    CancelDownload: Boolean;
    CanResume: Boolean;
    procedure DoDownload(Resuming: Boolean);
  public
    {User declarations}
  end;
 
var
  Form1: TForm1;
 
implementation
 
uses
  Sysutils, Windows;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  IdHTTP.ConnectTimeout := 70000;
  IdHTTP.ReadTimeout := 70000;
end;
 
procedue TForm1.DownloadClick(Sender: TObject);
begin
  DoDownload(False);
end;
 
procedure TForm1.CancelClick(Sender: TObject);
begin
  CancelDownload := True;
end;
 
procedure TForm1.ResumeClick(Sender: TObject);
begin
  DoDownload(True);
end;
 
procedure TForm1.IdHTTPHeadersAvailable(Sender: TObject; AHeaders: TIdHeaderList; 
var VContinue: Boolean);
begin
  if IdHTTP.Request.Method <> 'GET' then
    Exit;
 
  if (IdHTTP.Response.ResponseCode = 200) or (IdHTTP.Response.ResponseCode 
= 206) then
  begin
    CanResume := TextIsSame(AHeaders.Values['Accept-Ranges'], 'bytes');
    ProgressBar.Position := 0;
 
    if IdHTTP.Response.ResponseCode = 200 then
    begin
      if IdHTTP.Response.HasContentLength then
        ProgressBar.Max := IdHTTP.Response.ContentLength
      else
        ProgressBar.Max := 0;
 
      if (IdHTTP.Response.ContentStream <> nil) and (IdHTTP.Request.Ranges.Count 
<div class="jive-quote">0) then</div>
        IdHTTP.Response.ContentStream.Size := 0;
    end
    else if IdHTTP.Response.ContentRangeInstanceLength > -1 then
    begin
      ProgressBar.Max := IdHTTP.Response.ContentRangeInstanceLength;
      ProgressBar.Position := IdHTTP.Response.ContentRangeStart;
 
      Label.Caption := 'Bytes recv: ' + IntToStr(ProgressBar.Position);
    end
    else begin
      ProgressBar.Max := 0;
    end;
  end;
end;
 
pprocedure TForm1.IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount 
Int64);
var
  numBytes: Int64;
begin
  if CancelDownload then
    Sysutils.Abort; // cancel the download
 
  if IdHTTP.Request.Method <> 'GET' then
    Exit;
 
  if (AWorkMode = wmRead) and (IdHTTP.Response.ContentStream <> nil) then
  begin
    {if AWorkCount >= (1024*1024*3) then
      raise Exception.Create('simulated break'); // simulate a broken download 
every ~3MB}
 
    numBytes := IdHTTP.Response.ContentStream.Position;
 
    if ProgressBar.Max > 0 then
      ProgressBar.Position := numBytes;
 
    Label.Caption := 'Bytes recv: ' + IntToStr(numBytes);
  end;
 
  if GetQueueStatus(QS_ALLINPUT) <> 0 then
    Application.ProcessMessages;
end;
 
procedure TForm1DoDownload(Resuming: Boolean);
var
  URL, FileName, MyFile: string; 
  FS: TFileStream;
  pos: Int64;
begin
  Download.Enabled := False;
  Resume.Enabled := False;
  Cancel.Enabled := False;
  CanResume := False;
 
  ProgressBar.Position := 0;
  Label.Caption := 'Bytes recv: -';
 
  Update;
 
  try
    URL := Edit1.Text;
 
    if (not Resuming) or (SaveDialog.FileName = '') then
    begin
      Label.Caption := 'Requesting filename...';
      Label.Update;
 
      IdHTTP.Head(URL);
 
      FileName := IdHTTP.Response.RawHeaders.Params['Content-Disposition', 
'filename'];
      if FileName = '' then
      begin
        FileName := IdHTTP.Response.RawHeaders.Params['Content-Type', 'name'];
        if FileName = '' then
          FileName := IdHTTP.URL.Document;
      end;
 
      SaveDialog.FileName := FileName;
      if not SaveDialog->Execute() then
        Exit;
 
      Label.Caption := 'Bytes recv: -';
      Label.Update;
    end;
 
    MyFile := SaveDialog.FileName;
 
    try
      try
        if Resuming then
          FS := TFileStream.Create(MyFile, fmOpenReadWrite or fmShareDenyWrite)
        else
          FS := TFileStream.Create(MyFile, fmCreate or fmShareDenyWrite);
      except
        if Resuming then
        begin
          Resuming := False;
          FS := TFileStream.Create(MyFile, fmCreate or fmShareDenyWrite);
        end
        else
          raise;
      end;
 
      try
        CancelDownload := False;
        Cancel.Enabled := True;
 
        IdHTTP.Request.Clear;
 
        if Resuming then
        begin
          FS.Seek(0, soFromEnd);
          pos := FS.Position;
          if pos > 0 then
            IdHTTP.Request.Ranges.Add.StartPos := pos;
        end;
 
        IdHTTP.Get(URL, FS);
        CanResume := False;
      finally
        FS.Free;
      end;
 
      ProgressBar.Position := ProgressBar.Max;
      ShowMessage('Download finished');
    except
      on E: EAbort do
      begin
        ShowMessage('Download cancelled');
      end;
      on E: Exception do
      begin
        ShowMessage('Download failed');
      end;
    end;
  finally
    Download.Enabled := True;
    Resume.Enabled := CanResume;
    Cancel.Enabled := False;
  end;
end;
 
end.


--
Remy Lebeau (TeamB)
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 29, 2016 1:57 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks a million for your efforts Remy. I really appreciate it. I need to ask another question. Is IdHTTP thread-safe? If not can it be put inside a thread? I must confess that I'm a bit of a novice with IdHTTP & I've not used it at all before now.

JayDee

Remy Lebeau (TeamB) wrote:
Jay wrote:

What is if (GetQueueStatus(QS_ALLINPUT) != 0)

GetQueueStatus() is a Microsoft Win32 API function:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms644940.aspx

how does it work with Application.ProcessMessages

The code is using GetQueueStatus() to check if the calling thread's message
queue has any pending messages that need to be processed. There is no point
in calling ProcessMessages() if there is nothing to process. And besides,
calling ProcessMessages() too often, especially in a loop, can cause performance
issues.

finally how do I convert it to Delphi/FreePascal?

Something like this:

unit Unit1;
 
interface
 
uses
  Classes, Controls, StdCtrls, Forms, Dialogs, IdHeaderList, ComCtrls, IdBaseComponent, 
IdComponent, IdHTTP, IdTCPClient, IdTCPConnection, IdSSLOpenSSL, IdIOHandler, 
IdIOHandlerSocket, IdIOHandlerStack, IdSSL;
 
type
  TForm1 = class(TForm)
    Download: TButton;
    IdHTTP: TIdHTTP;
    IdSSL: TIdSSLIOHandlerSocketOpenSSL;
    SaveDialog: TSaveDialog;
    Edit1: TEdit;
    ProgressBar: TProgressBar;
    Cancel: TButton;
    Resume: TButton;
    Label: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure DownloadClick(Sender: TObject);
    procedure IdHTTPHeadersAvailable(Sender: TObject; AHeaders: TIdHeaderList; 
var VContinue: Boolean);
    procedure IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: 
Int64);
    procedure CancelClick(Sender: TObject);
    procedure ResumeClick(Sender: TObject);
  private
    {User declarations}
    CancelDownload: Boolean;
    CanResume: Boolean;
    procedure DoDownload(Resuming: Boolean);
  public
    {User declarations}
  end;
 
var
  Form1: TForm1;
 
implementation
 
uses
  Sysutils, Windows;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  IdHTTP.ConnectTimeout := 70000;
  IdHTTP.ReadTimeout := 70000;
end;
 
procedue TForm1.DownloadClick(Sender: TObject);
begin
  DoDownload(False);
end;
 
procedure TForm1.CancelClick(Sender: TObject);
begin
  CancelDownload := True;
end;
 
procedure TForm1.ResumeClick(Sender: TObject);
begin
  DoDownload(True);
end;
 
procedure TForm1.IdHTTPHeadersAvailable(Sender: TObject; AHeaders: TIdHeaderList; 
var VContinue: Boolean);
begin
  if IdHTTP.Request.Method <> 'GET' then
    Exit;
 
  if (IdHTTP.Response.ResponseCode = 200) or (IdHTTP.Response.ResponseCode 
= 206) then
  begin
    CanResume := TextIsSame(AHeaders.Values['Accept-Ranges'], 'bytes');
    ProgressBar.Position := 0;
 
    if IdHTTP.Response.ResponseCode = 200 then
    begin
      if IdHTTP.Response.HasContentLength then
        ProgressBar.Max := IdHTTP.Response.ContentLength
      else
        ProgressBar.Max := 0;
 
      if (IdHTTP.Response.ContentStream <> nil) and (IdHTTP.Request.Ranges.Count 
<div class="jive-quote">0) then</div>
        IdHTTP.Response.ContentStream.Size := 0;
    end
    else if IdHTTP.Response.ContentRangeInstanceLength > -1 then
    begin
      ProgressBar.Max := IdHTTP.Response.ContentRangeInstanceLength;
      ProgressBar.Position := IdHTTP.Response.ContentRangeStart;
 
      Label.Caption := 'Bytes recv: ' + IntToStr(ProgressBar.Position);
    end
    else begin
      ProgressBar.Max := 0;
    end;
  end;
end;
 
pprocedure TForm1.IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount 
Int64);
var
  numBytes: Int64;
begin
  if CancelDownload then
    Sysutils.Abort; // cancel the download
 
  if IdHTTP.Request.Method <> 'GET' then
    Exit;
 
  if (AWorkMode = wmRead) and (IdHTTP.Response.ContentStream <> nil) then
  begin
    {if AWorkCount >= (1024*1024*3) then
      raise Exception.Create('simulated break'); // simulate a broken download 
every ~3MB}
 
    numBytes := IdHTTP.Response.ContentStream.Position;
 
    if ProgressBar.Max > 0 then
      ProgressBar.Position := numBytes;
 
    Label.Caption := 'Bytes recv: ' + IntToStr(numBytes);
  end;
 
  if GetQueueStatus(QS_ALLINPUT) <> 0 then
    Application.ProcessMessages;
end;
 
procedure TForm1DoDownload(Resuming: Boolean);
var
  URL, FileName, MyFile: string; 
  FS: TFileStream;
  pos: Int64;
begin
  Download.Enabled := False;
  Resume.Enabled := False;
  Cancel.Enabled := False;
  CanResume := False;
 
  ProgressBar.Position := 0;
  Label.Caption := 'Bytes recv: -';
 
  Update;
 
  try
    URL := Edit1.Text;
 
    if (not Resuming) or (SaveDialog.FileName = '') then
    begin
      Label.Caption := 'Requesting filename...';
      Label.Update;
 
      IdHTTP.Head(URL);
 
      FileName := IdHTTP.Response.RawHeaders.Params['Content-Disposition', 
'filename'];
      if FileName = '' then
      begin
        FileName := IdHTTP.Response.RawHeaders.Params['Content-Type', 'name'];
        if FileName = '' then
          FileName := IdHTTP.URL.Document;
      end;
 
      SaveDialog.FileName := FileName;
      if not SaveDialog->Execute() then
        Exit;
 
      Label.Caption := 'Bytes recv: -';
      Label.Update;
    end;
 
    MyFile := SaveDialog.FileName;
 
    try
      try
        if Resuming then
          FS := TFileStream.Create(MyFile, fmOpenReadWrite or fmShareDenyWrite)
        else
          FS := TFileStream.Create(MyFile, fmCreate or fmShareDenyWrite);
      except
        if Resuming then
        begin
          Resuming := False;
          FS := TFileStream.Create(MyFile, fmCreate or fmShareDenyWrite);
        end
        else
          raise;
      end;
 
      try
        CancelDownload := False;
        Cancel.Enabled := True;
 
        IdHTTP.Request.Clear;
 
        if Resuming then
        begin
          FS.Seek(0, soFromEnd);
          pos := FS.Position;
          if pos > 0 then
            IdHTTP.Request.Ranges.Add.StartPos := pos;
        end;
 
        IdHTTP.Get(URL, FS);
        CanResume := False;
      finally
        FS.Free;
      end;
 
      ProgressBar.Position := ProgressBar.Max;
      ShowMessage('Download finished');
    except
      on E: EAbort do
      begin
        ShowMessage('Download cancelled');
      end;
      on E: Exception do
      begin
        ShowMessage('Download failed');
      end;
    end;
  finally
    Download.Enabled := True;
    Resume.Enabled := CanResume;
    Cancel.Enabled := False;
  end;
end;
 
end.


--
Remy Lebeau (TeamB)
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: IdHTTP download from Dropbox  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 29, 2016 2:53 PM   in response to: Jay Dee in response to: Jay Dee
Jay wrote:

Is IdHTTP thread-safe?

No more than any other component is. It can certainly be used in the main
UI thread, if that is what you are worried about. Of course, it will block
the main UI message loop while it is busy accessing the HTTP server. You
can use a TIdAntiFreeze component to let the main UI message queue continue
to proess messages while TIdHTTP is busy (the examples I posted earlier do
no use TIdAntiFreeze, but if you added it then you can remove the GetQueuedStatus()
check and Application.ProcessMessages() call).

If not can it be put inside a thread?

Yes. In fact, the majority of Indy is designed to be usable in worker threads,
and should usually be used in worker threads to avoid UI blockages.

--
Remy Lebeau (TeamB)
Jay Dee

Posts: 44
Registered: 11/22/10
Re: IdHTTP download from Dropbox [Edit] [Edit] [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 3, 2016 8:55 AM   in response to: Jay Dee in response to: Jay Dee
With the assistance of everyone who replied to this thread and the example on this web site

http://etutorials.org/Programming/mastering+delphi+7/Part+IV+Delphi+the+Internet+and+a+.NET+Preview/Chapter+19+Internet+Programming+Sockets+and+Indy/Working+with+HTTP/,

I've been able to write a multithreaded version of the file downloader.

I've learned a lot in the process.

Thanks again,

JayDee

Edited by: Jay Dee on Mar 3, 2016 5:55 PM

Edited by: Jay Dee on Mar 3, 2016 11:30 PM
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02