No minule jsem byl k THTTPClient trošku nespravedlivý, ve skutečnosti toho umí více než se zdálo. Kromě https bez nutnosti dodání OpenSSL pro různé platformy to má zajímavé možnosti ohledně paralelního zpracování. Ukáži na malém příkladu.
Ukáži nejprve příklad na zamyšlení a pak vysvětlím co to vlastně dělá.
implementation
uses
System.Threading,
System.Net.httpclient;
procedure TForm9.AllocateFile(const sUrl, sFile: string; var iSize: Integer);
var
oClient: THTTPClient;
oResponse: IHTTPResponse;
oFile : TFileStream;
begin
oClient := THTTPClient.Create;
try
if not oClient.CheckDownloadResume(sUrl) then
begin
ShowMessage('Not support resume, parallel demo abort, use Get method'); // download direct via oClient.Get
Abort;
end;
oResponse := oClient.Head(sUrl);
oFile := TFileStream.Create(sFile, fmCreate);
try
iSize := oResponse.ContentLength;
oFile.Size := iSize;
finally
oFile.Free;
end;
finally
oClient.Free;
end;
end;
procedure TForm9.Button1Click(Sender: TObject);
var
iSize: Integer;
const
csUrl = 'http://vesta.informatik.rwth-aachen.de/ftp/pub/comp/Linux/knoppix/knoppix-cd/KNOPPIX_V7.2.0bootonly-2013-06-16-EN.iso';
csFile = 'c:\temp\temp.iso';
ciSlice = 10;
begin
AllocateFile(csUrl, csFile, iSize);
TParallel.&For(0, ciSlice - 1,
procedure (iIndex: Integer)
var
iPartSize, iStart, iEnd: Int64;
oClient: THTTPClient;
oFile: TFileStream;
LResponse: IHTTPResponse;
begin
// zjisti jaký blok mám vlastně stahovat
iPartSize := iSize div ciSlice;
iStart := iPartSize * iIndex;
iEnd := iPartSize * (iIndex + 1);
if iIndex = ciSlice - 1 then // pro poslední blok malá korekce
iEnd := iSize;
oClient := THTTPClient.Create;
try
oFile := TFileStream.Create(csFile, fmOpenWrite or fmShareDenyNone);
try
oFile.Seek(iStart, soBeginning); // nastav se v souboru na místo
LResponse := oClient.GetRange(csUrl, iStart, iEnd - 1, oFile); // get it!
finally
oFile.Free;
end;
finally
oClient.Free;
end;
end
);
ShowMessage('Downloaded!');
end;
end.
Kromě THttpClient kód používá možností knihovny System.Threading z XE7: anonymní metodu pouštěnou 10x za pomocí TParallel.For (ten znak & není chyba, ale možnost jak napsat klíčové slovo). Každá instance anonymní metody stáhne svoji část dat do připraveného souboru a celek počká na dokončení všech úloh a pak zahlásí informaci o stažení.
Ano, mohl jsem použít TThread přímo, ale není to takto více čitelnější?
Pro upřesnění: metoda AllocateFile vytvoří na disku prázdný soubor o velikosti budoucího stahovaného souboru. Velikost se získá za pomoci volání THttpClient.Head, kdy se vrací informace o stahovatelných datech. Nejprve se ale zjistí, zda server vůbec podporuje Resume u HTTP (metoda CheckDownloadResume), pokud ne, tak se demo ukončí - na normálním stahování není nic zvláštního.
Samozřejmě si jako vždy rád přečtu Vaše názory v komentářích.
Update: Igor mi napsal o nějakém problému v XE8.1, opraveno v Delphi 10
V XE8 Update 1 je nějaká ptákovina, takže na některých Windows (třeba Windows Server 2008) se ztrácejí parametry u metody GET. Možná by bylo dobré přidat tam komentář, aby si někdo nenabil jako já. Přepsal jsem asi 20 unit, všechno šlapalo jak hodinky, pak to dám na server a konečná. Ztrávil jsem tím několik báječných hodin a na nic jsem nepřišel. Prostě to nechodí. Příslušný server hlásí, že nedostal některé parametry. U mě se to projevilo u GET ve spojení s HTTPS, ale našel jsem report v QC, kde to dalším lidem zlobí zřejmě i na HTTP. Dle toho reportu je tam nějaký bug zanesený v update 1, protože stejný kód jim v XE8 chodil.
quality.embarcadero.com/browse/RSP-11479