2010-08-23 19 views
7

Me gustaría descargar un archivo de mi programa Delphi en un hilo separado dedicado a la descarga.Descargar un archivo de Internet mientras se puede cancelar la descarga en cualquier momento

El problema es que el programa principal se puede cerrar en cualquier momento (por lo tanto, el hilo de descarga puede terminarse en cualquier momento). Entonces, incluso si no hay conexión o el servidor se demora en segundos, necesito una forma de terminar el hilo de descarga dentro de uno o dos segundos.

¿Qué función me recomiendan usar?

He experimentado con InterOpenURL/InternetReadFile pero no tiene un parámetro de tiempo de espera. Tiene una versión asíncrona pero no pude encontrar ningún ejemplo de Delphi en funcionamiento y no estoy seguro de si la versión asíncrona me protegerá de bloqueos ...

Me han recomendado usar sockets, pero TClientSocket no Parece que tampoco tiene una función de tiempo de espera.

Necesito estar totalmente preparado, por lo que si el usuario tiene problemas de conexión a Internet en su computadora o si el servidor web se queda rezagado, mi aplicación no se bloquea antes de cerrar.

Tenga en cuenta al responder que no quiero utilizar ningún componente de terceros ni Indy. Cualquier código de ejemplo es muy apreciado.

¡Gracias!

+0

El 'InternetReadFile' puede ser llamada con una cantidad arbitraria de bytes que se debe buscar en un bucle. Si combina esto con un cheque Terminado en su hilo, marque el hilo para la terminación al cerrar la aplicación y espere a que termine el hilo, no debe tener ningún "cuelga". –

Respuesta

2

TWinSocketStream tiene un tiempo de espera. Básicamente, crea un socket de bloqueo y luego crea un TWinSocketStream utilizando ese socket para leer/escribir. (algo así como usar un StreamWriter). A continuación, realiza un bucle hasta que se cierra la conexión, finaliza el subproceso o alcanza la cantidad de bytes esperados. Hay un WaitForData() que le permite verificar los datos entrantes con un tiempo de espera, por lo que nunca estará 'bloqueado' más allá de ese valor de tiempo de espera.

Sin embargo, tendrá que manejar el proceso http usted mismo. es decir, envíe el 'GET', y luego lea el encabezado de respuesta para determinar cuántos bytes esperar, pero eso no es demasiado difícil.

+0

¿Tiene algún código de ejemplo? :-) – Steve

+1

Desgraciadamente, no personalmente. Generalmente uso la biblioteca de sinapsis para este tipo de cosas. Encontré esto en línea, parece correcto, pero YMMV. http://delphi.xcjc.net/viewthread.php?tid=29580 Este ejemplo usa un socketthread, pero el principio sería el mismo: espere datos, lea datos, salga si se agota el tiempo. – GrandmasterB

5

Esto funciona.

var 
    DownloadAbort: boolean; 

procedure Download(const URL, FileName: string); 
var 
    hInet: HINTERNET; 
    hURL: HINTERNET; 
    Buffer: array[0..1023] of AnsiChar; 
    BufferLen, amt: cardinal; 
    f: file; 
const 
    UserAgent = 'Delphi Application'; // Change to whatever you wish 
begin 
    DownloadAbort := false; 
    hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); 
    try 
    hURL := InternetOpenUrl(hInet, PChar(URL), nil, 0, 0, 0); 
    try 
     FileMode := fmOpenWrite; 
     AssignFile(f, FileName); 
     try 
     Rewrite(f, 1); 
     repeat 
      InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen); 
      BlockWrite(f, Buffer[0], BufferLen, amt); 
      if DownloadAbort then 
      Exit; 
     until BufferLen = 0; 
     finally 
     CloseFile(f); 
     end; 
    finally 
     InternetCloseHandle(hURL); 
    end; 
    finally 
    InternetCloseHandle(hInet); 
    end; 
end; 

En la práctica, el código anterior será parte del método de su hilo Execute, y usted no necesita DownloadAbort; intead, se echa

if Terminated then 
    Exit; 
+0

¡Hola! El código anterior no funcionará si InternetReadFile se cuelga. Antes de publicar mi pregunta, hojeé durante horas y leí informes de que esto estaba sucediendo. – Steve

+0

@Steve: ¿Alguna vez * usted * observó el bloqueo de 'InternetReadFile'? Por lo general, puedes confiar en la API de Windows. Después de todo, es usado todos los días por millones de desarrolladores. –

+0

No es difícil encontrar información al respecto, consulte este por ejemplo: http://www.codeguru.com/forum/archive/index.php/t-355462.html Pero si lo piensa, hay Hay muchas partes en las que puede salir mal: problema con la conexión a Internet del usuario, un proxy que no funciona, problema con el enrutador wifi, respuesta lenta del servidor de Internet ... Creo que incluso InternetOpenURL podría seguir colgando. Necesito funciones que no esperen una respuesta, pero que den control de regreso en un instante y permitan la cancelación si es necesario. ¿Alguna idea? – Steve

2

Gracias a GrandmasterB, aquí está el código que he logrado armar: (está en el bloque de ejecutar mi hilo)

var 
    Buffer: array [0..1024 - 1] of Char; 
    SocketStream: TWinSocketStream; 
    RequestString: String; 
    BytesRead: Integer; 
begin 
    try 
    ClientSocket.Active := true; 
    DownloadedData := ''; 
    FillChar(Buffer, SizeOf(Buffer), #0); 

    if not Terminated then 
    begin 
     // Wait 10 seconds 
     SocketStream := TWinSocketStream.Create(ClientSocket.Socket, 10000); 
     try 
     // Send the request to the webserver 
     RequestString := 'GET /remotedir/remotefile.html HTTP/1.0'#13#10 + 
      'Host: www.someexamplehost.com'#13#10#13#10; 
     SocketStream.Write(RequestString[1], Length(RequestString)); 

     // We keep waiting for the data for 5 seconds 
     if (not Terminated) and SocketStream.WaitForData(5000) then 
      repeat 
      // We store the data in our buffer 
      BytesRead := SocketStream.Read(Buffer, SizeOf(Buffer)); 

      if BytesRead = 0 then 
       break 
      else 
       DownloadedData := DownloadedData + Buffer; 
      until Terminated or (not SocketStream.WaitForData(2000)); 
     finally 
     // Close the connection 
     SocketStream.Free; 
     if ClientSocket.Active then 
      ClientSocket.Active := false; 
     end; 
    end; 
    except 
    end; 

Hay que poner esto al constructor de la rosca:

ClientSocket := TClientSocket.Create(nil); 
    ClientSocket.Host := 'www.someexamplehost.com'; 
    ClientSocket.Port := 80; 
    ClientSocket.ClientType := ctBlocking; 

    inherited Create(false); // CreateSuspended := false; 

Y esto a la desctructor:

ClientSocket.Free; 
    inherited; 
+0

¡Eso se ve bien! La respuesta tendrá encabezados de respuesta http en ellos, por lo que el archivo que reciba comenzará en realidad después de dos # D # A consecutivos, que separan el encabezado de los datos. Puede cerrar el zócalo tan pronto como reciba el número de bytes especificado en el encabezado de respuesta, lo que evitará que los últimos 5 segundos se cuelguen mientras espera para ver si hay más datos disponibles. – GrandmasterB

1

de delphi.about.com


uses ExtActns, ... 

type 
    TfrMain = class(TForm) 
    ... 
    private 
    procedure URL_OnDownloadProgress 
     (Sender: TDownLoadURL; 
     Progress, ProgressMax: Cardinal; 
     StatusCode: TURLDownloadStatus; 
     StatusText: String; var Cancel: Boolean) ; 
    ... 

implementation 
... 

procedure TfrMain.URL_OnDownloadProgress; 
begin 
    ProgressBar1.Max:= ProgressMax; 
    ProgressBar1.Position:= Progress; 
end; 

function DoDownload; 
begin 
    with TDownloadURL.Create(self) do 
    try 
    URL:='http://0.tqn.com/6/g/delphi/b/index.xml'; 
    FileName := 'c:\ADPHealines.xml'; 
    OnDownloadProgress := URL_OnDownloadProgress; 

    ExecuteTarget(nil) ; 
    finally 
    Free; 
    end; 
end; 

{ Nota: puntos de propiedad URL a Internet nombre de archivo es el archivo local }

Cuestiones relacionadas