delphi http download indy idhttp

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

  1. El descargador debe enviar solo una solicitud GET al servidor.
  2. 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!