delphi - Descargar archivo progresivamente con TIdHttp
download indy (3)
La variante n. ° 1 es la más sencilla, y es la forma en que Indy debe usarse.
En cuanto al problema de asignación de disco, puede derivar una nueva clase de TFileStream
y anular su método SetSize()
para no hacer nada. TIdHTTP
aún intentará asignar previamente el archivo cuando corresponda, pero en realidad no asignará ningún espacio de disco. Escribir en TFileStream
hará crecer el archivo según sea necesario.
Con respecto al informe de estado, TIdHTTP
tiene eventos OnWork...
para ese propósito. El parámetro AWorkCountMax
de OnWorkBegin
será el tamaño real del archivo si se conoce (la respuesta no se fragmenta), o 0 si no se conoce. El parámetro AWorkCount
del evento OnWork
será el número acumulado de bytes que se han transferido hasta el momento. Si se conoce el tamaño del archivo, puede mostrar el porcentaje total simplemente dividiendo el AWorkCount
por el AWorkCountMax
y multiplicando por 100, de lo contrario solo muestre el valor de AWorkCount
por sí mismo. Si desea visualizar la velocidad de la transferencia, puede calcularlo a partir de la diferencia de los valores de AWorkCount
y los intervalos de tiempo entre múltiples eventos de OnWork
.
Prueba esto:
type
TNoPresizeFileStream = class(TFileStream)
procedure
procedure SetSize(const NewSize: Int64); override;
end;
procedure TNoPresizeFileStream.SetSize(const NewSize: Int64);
begin
end;
.
type
TSomeClass = class(TSomething)
...
TotalBytes: In64;
LastWorkCount: Int64;
LastTicks: LongWord;
procedure Download;
procedure HttpWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
procedure HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
procedure HttpWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
...
end;
procedure TSomeClass.Download;
var
Buffer: TNoPresizeFileStream;
HttpClient: TIdHttp;
begin
Buffer := TNoPresizeFileStream.Create(''somefile.exe'', fmCreate or fmShareDenyWrite);
try
HttpClient := TIdHttp.Create(nil);
try
HttpClient.OnWorkBegin := HttpWorkBegin;
HttpClient.OnWork := HttpWork;
HttpClient.OnWorkEnd := HttpWorkEnd;
HttpClient.Get(''http://somewhere.com/somefile.exe'', Buffer); // wait until it is done
finally
HttpClient.Free;
end;
finally
Buffer.Free;
end;
end;
procedure TSomeClass.HttpWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
begin
if AWorkMode <> wmRead then Exit;
// initialize the status UI as needed...
//
// If TIdHTTP is running in the main thread, update your UI
// components directly as needed and then call the Form''s
// Update() method to perform a repaint, or Application.ProcessMessages()
// to process other UI operations, like button presses (for
// cancelling the download, for instance).
//
// If TIdHTTP is running in a worker thread, use the TIdNotify
// or TIdSync class to update the UI components as needed, and
// let the OS dispatch repaints and other messages normally...
TotalBytes := AWorkCountMax;
LastWorkCount := 0;
LastTicks := Ticks;
end;
procedure TSomeClass.HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
PercentDone: Integer;
ElapsedMS: LongWord;
BytesTransferred: Int64;
BytesPerSec: Int64;
begin
if AWorkMode <> wmRead then Exit;
ElapsedMS := GetTickDiff(LastTicks, Ticks);
if ElapsedMS = 0 then ElapsedMS := 1; // avoid EDivByZero error
if TotalBytes > 0 then
PercentDone := (Double(AWorkCount) / TotalBytes) * 100.0;
else
PercentDone := 0.0;
BytesTransferred := AWorkCount - LastWorkCount;
// using just BytesTransferred and ElapsedMS, you can calculate
// all kinds of speed stats - b/kb/mb/gm per sec/min/hr/day ...
BytesPerSec := (Double(BytesTransferred) * 1000) / ElapsedMS;
// update the status UI as needed...
LastWorkCount := AWorkCount;
LastTicks := Ticks;
end;
procedure TSomeClass.HttpWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
if AWorkMode <> wmRead then Exit;
// finalize the status UI as needed...
end;
Quiero implementar un descargador de http simple usando TIdHttp (Indy10). Encontré dos tipos de ejemplos de código de internet. Lamentablemente, ninguno de ellos me satisface al 100%. Aquí está el código y quiero un consejo.
Variante 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;
El código es compacto y muy fácil de entender. El problema es que asigna espacio en disco cuando comienza la descarga. Otro problema es que no podemos mostrar el progreso de la descarga en GUI directamente, a menos que el código se ejecute en un hilo de fondo (alternativamente, podemos enlazar el evento HttpClient.OnWork).
Variante 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;
Primero consultamos el tamaño del archivo desde el servidor y luego descargamos el contenido del archivo en pedazos. El contenido del archivo recuperado se guardará en el disco cuando se reciban por completo. El problema potencial es que tenemos que enviar múltiples solicitudes GET al servidor. No estoy seguro de si algunos servidores (como megaupload) pueden limitar el número de solicitudes dentro de un período de tiempo determinado.
Mis expectativas
- El descargador debe enviar solo una solicitud GET al servidor.
- El espacio en disco no debe asignarse cuando comienza la descarga.
Cualquier sugerencia es apreciada.
No olvides agregar esto para la Variante 2
: Else HttpClient.Request.ContentRangeEnd := FileSize;
Reemplazar
if Buffer.Size + RECV_BUFFER_SIZE < FileSize then
HttpClient.Request.ContentRangeEnd := Buffer.Size + RECV_BUFFER_SIZE - 1;
Por
if Buffer.Size + RECV_BUFFER_SIZE < FileSize then
HttpClient.Request.ContentRangeEnd := Buffer.Size + RECV_BUFFER_SIZE - 1;
Else HttpClient.Request.ContentRangeEnd := FileSize;
Aquí hay un ejemplo que muestra cómo usar los componentes OnWork para mostrar una barra de progreso:
Descargue un archivo de Internet programáticamente con un evento Progress usando Delphi e Indy
No debe preocuparse por la asignación del disco. El espacio en disco que se asigna no está realmente escrito, por lo que no dañará sus discos. ¡Sea feliz de que esté asignado para que no sea posible que otro proceso reclame el espacio en disco y le permita quedarse sin espacio!