I want to implement a simple http downloader using TIdHttp (Indy10). I found two kind of code examples from the internet. Unfortunately none of them satisfy me 100%. Here is the code and I want some advise.
Variant 1
var
Buffer: TFileStream;
HttpClient: TIdHttp;
begin
Buffer := TFileStream.Create('somefile.exe', fmCreate or fmShareDenyWrite);
try
HttpClient := TIdHttp.Create(nil);
try
HttpClient.Get('http://somewhere.com/somefile.exe', Buffer); // wait until it is done
finally
HttpClient.Free;
end;
finally
Buffer.Free;
end;
end;
The code is compact and very easy to understand. The problem is that it allocates disk space when downloading begins. Another problem is that we cannot show the download progress in GUI directly, unless the code is executed in a background thread (alternatively we can bind HttpClient.OnWork event).
Variant 2:
const
RECV_BUFFER_SIZE = 32768;
var
HttpClient: TIdHttp;
FileSize: Int64;
Buffer: TMemoryStream;
begin
HttpClient := TIdHttp.Create(nil);
try
HttpClient.Head('http://somewhere.com/somefile.exe');
FileSize := HttpClient.Response.ContentLength;
Buffer := TMemoryStream.Create;
try
while Buffer.Size < FileSize do
begin
HttpClient.Request.ContentRangeStart := Buffer.Size;
if Buffer.Size + RECV_BUFFER_SIZE < FileSize then
HttpClient.Request.ContentRangeEnd := Buffer.Size + RECV_BUFFER_SIZE - 1
else
HttpClient.Request.ContentRangeEnd := FileSize;
HttpClient.Get(HttpClient.URL.URI, Buffer); // wait until it is done
Buffer.SaveToFile('somefile.exe');
end;
finally
Buffer.Free;
end;
finally
HttpClient.Free;
end;
end;
First we query the file size from the server and then we download file contents in pieces. Retrieved file contents will be save to disk when they are received completely. The potential problem is we have to send multiple GET requests to the server. I am not sure if some servers (such as megaupload) might limit the number of requests within particular time period.
My expectations
- The downloader should send only one GET-request to the server.
- The disk space must not be allocated when the download begins.
Any hints are appreciated.
Here is an example that shows how to use the components OnWork to show a progress bar:
Download a File from internet programatically with an Progress event using Delphi and Indy
You should not worry about the disk allocation. Disk space that is allocated is not actually written to, so it won't damage your disks. Be happy that it is allocated so that it is not possible that another process claims the disk space and let you run out of space!
Variant #1 is the simpliest, and is how Indy is meant to be used.
Regarding the disk allocation issue, you can derive a new class from
TFileStream
and override itsSetSize()
method to do nothing.TIdHTTP
will still attempt to pre-allocate the file when appropriate, but it will not actually allocate any disk space. Writing toTFileStream
will grow the file as needed.Regarding status reporting,
TIdHTTP
hasOnWork...
events for that purpose. TheAWorkCountMax
parameter of theOnWorkBegin
will be the actual file size if known (the response is not chunked), or 0 if not known. TheAWorkCount
parameter of theOnWork
event will be the cumulative number of bytes that have been transferred so far. If the file size is known, you can display the total percentage by simply dividing theAWorkCount
by theAWorkCountMax
and multiplying by 100, otherwise just display theAWorkCount
value by itself. If you want to display the speed of the transfer, you can calculate that from the difference ofAWorkCount
values and the time intervals between multipleOnWork
events.Try this:
.
Do not forget to add this for the Variant 2
Replace
By