Quando un'applicazione Delphi deve scaricare o caricare file da un server, il componente TIdHTTP della libreria Indy è quasi sempre la scelta più diretta: fa parte della distribuzione standard di Delphi e gestisce da solo buona parte delle complessità del protocollo HTTP. Il problema arriva quando serve mostrare all'utente l'avanzamento dell'operazione: la soluzione più pulita è creare una classe discendente che intercetta gli eventi di progresso di Indy.
Estendere TIdHTTP con una property ProgressBar
L'idea di base è semplice: incapsulare un TProgressBar dentro una classe che eredita da TIdHTTP, e aggiornarlo automaticamente durante il trasferimento.
unit HttpConAvanzamento;
interface
uses
System.Classes, IdHTTP, IdComponent, Vcl.ComCtrls;
type
TCustomHTTP = class(TIdHTTP)
private
FProgressBar: TProgressBar;
procedure GestisciAvanzamento(ASender: TObject;
const ABytesTransferred, AContentLength: Int64);
public
constructor Create(AOwner: TComponent; AProgressBar: TProgressBar); reintroduce;
property ProgressBar: TProgressBar read FProgressBar write FProgressBar;
end;
implementation
constructor TCustomHTTP.Create(AOwner: TComponent; AProgressBar: TProgressBar);
begin
inherited Create(AOwner);
FProgressBar := AProgressBar;
end;
procedure TCustomHTTP.GestisciAvanzamento(ASender: TObject;
const ABytesTransferred, AContentLength: Int64);
begin
if Assigned(FProgressBar) then
begin
FProgressBar.Max := AContentLength;
FProgressBar.Position := ABytesTransferred;
end;
end;
end.
L'evento da cui arrivano queste informazioni è OnWork (in alcune versioni affiancato da OnWorkBegin e OnWorkEnd, utili per resettare la barra a inizio e fine operazione): Indy lo invoca periodicamente durante la lettura o scrittura dello stream, passando i byte trasferiti e — quando il server la dichiara nell'header — la dimensione totale del contenuto.
Aggiungere l'upload con IdMultipartFormData
La stessa classe può gestire anche l'upload, tipicamente in multipart/form-data, distinguendo l'evento di progresso per le due direzioni tramite due handler separati assegnati prima di ogni operazione:
procedure TCustomHTTP.ScaricaFile(const URL, NomeFile: string);
begin
OnWork := GestisciAvanzamentoDownload;
Get(URL, NomeFile);
end;
procedure TCustomHTTP.CaricaFile(const URL, NomeFile: string);
var
DatiForm: TIdMultipartFormDataStream;
begin
OnWork := GestisciAvanzamentoUpload;
DatiForm := TIdMultipartFormDataStream.Create;
try
DatiForm.AddFile('file', NomeFile, '');
Post(URL, DatiForm);
finally
DatiForm.Free;
end;
end;
Nell'uso reale conviene anche avvolgere le chiamate a Get e Post in un blocco try..except che intercetti EIdHTTPProtocolException (per gli errori HTTP restituiti dal server, come 404 o 500) ed EIdSocketError (per problemi di rete o timeout), così l'interfaccia può mostrare un messaggio comprensibile invece di lasciar propagare l'eccezione grezza di Indy.
Un dettaglio che conviene non dimenticare
Se TCustomHTTP viene usato dentro un TThread secondario, ricorda che l'aggiornamento della TProgressBar deve comunque avvenire nel thread principale: va incapsulato in una TThread.Queue o TThread.Synchronize, altrimenti si rischiano errori intermittenti e difficili da riprodurre sull'interfaccia grafica.
Con questa struttura hai una classe HTTP riutilizzabile in tutta l'applicazione, che si comporta come TIdHTTP ma con il feedback visivo integrato — utile per form di importazione dati, aggiornamento automatico o sincronizzazione con servizi esterni.