Ir al contenido


Foto

Cómo resumir una descarga?


  • Por favor identifícate para responder
9 respuestas en este tema

#1 cHackAll

cHackAll

    Advanced Member

  • Administrador
  • 599 mensajes

Escrito 30 abril 2009 - 02:29

Esperando no aburrir con el tema de downloaders, les dejo un pequeño programa que me fue util cuando el famoso Firefox se fue de vacaciones justo cuando estaba a media descarga de un archivo de tamaño considerable que obviamente quedó truncado.



delphi
  1. program Resume;
  2.  
  3. uses Windows, WinInet;
  4.  
  5. function _itoa(Value: Integer; lpBuffer: PChar; Radix: Integer): PChar; cdecl external 'ntdll';
  6.  
  7. var
  8. hInet, hConnect, hRequest: Pointer;
  9. hFile, Fail, Remainder, uBytes, dwReserved, Size: Cardinal;
  10. Buffer: array [1..16384] of Cardinal; // 64 Kb. (server is crazy?)
  11.  
  12. begin Dec(Remainder);
  13. Size := SizeOf(Remainder);
  14.  
  15. hInet := InternetOpen('agent', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  16. hConnect := InternetConnect(hInet, 'ufpr.dl.sourceforge.net', INTERNET_DEFAULT_HTTP_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
  17. hFile := CreateFile('Portable_Ubuntu.exe', GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0); // will be closed with the process
  18.  
  19. Fail := 0;
  20. repeat Sleep(Fail * 3000);
  21.   hRequest := HttpOpenRequest(hConnect, nil, 'sourceforge/portableubuntu/Portable_Ubuntu.exe', nil, nil, nil, INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_DONT_CACHE, 0);
  22.   lstrcat(_itoa(SetFilePointer(hFile, 0, nil, FILE_END), @lstrcpy(@Buffer, 'range: bytes=')[13], 10), '-');
  23.   HttpAddRequestHeaders(hRequest, @Buffer, lstrlen(@Buffer), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
  24.   if HttpSendRequest(hRequest, nil, 0, nil, 0) then
  25.   begin
  26.     HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH or HTTP_QUERY_FLAG_NUMBER, @Remainder, Size, dwReserved);
  27.     while (Remainder > 0) and InternetQueryDataAvailable(hRequest, Size, 0, 0) and (Size > 0) do
  28.     begin
  29.       InternetReadFile(hRequest, @Buffer, Size, uBytes);
  30.       Dec(Remainder, _lwrite(hFile, @Buffer, uBytes));
  31.       Fail := 0;
  32.     end;
  33.   end;
  34.   Inc(Fail);
  35.   InternetCloseHandle(hRequest);
  36. until (Remainder = 0) or (Fail = 3);
  37.  
  38. InternetCloseHandle(hConnect);
  39. InternetCloseHandle(hInet);
  40.  
  41. if Remainder <> 0 then
  42.   MessageBox(0, 'Download incomplete!'#13'Try again later.', nil, MB_ICONEXCLAMATION);
  43. end.



Lo comparto sin ningun maquillaje para que le extraigan la idea, pues es la semilla para crear un acelerador de descargas por multiples secciones.

Enjoy
  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 30 abril 2009 - 03:20

Hola

Realice una prueba y note que si lo cancelas y reinicias el "download" continua la descarga en el bloque que se quedo.

Por el lado de multiples secciones pues se lo dejo a los que saben de esto :p

Hay algunas adecuaciones que se me ocurren en este momento por ejemplo

  • Mostrar el avance de la descarga.
  • Utilizarlo para incluirlo en mis programas para que bajen actualizaciones desde mi página.

Muy bueno little bro (y).

Salud OS
  • 0

#3 joseme

joseme

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 62 mensajes
  • LocationCosta Rica

Escrito 01 mayo 2009 - 05:24

Hola! la prueba funcionó, voy a estudiar la posibilidad de integrarlo a un proyecto de backup/restore/copia remota que estoy encarando. Gracias, amigo.
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 01 mayo 2009 - 11:12

Muy buen aporte, cHackAll (y)

Una pregunta: ¿No se podrí­a hacer algo similar usando la API URLDownloadToFile apoyándose en IBindStatusCallback Interface en los métodos OnProgress y OnDataAvailable?

Con esa API se puede parar y reanudar la descarga y tenemos información de su estado. Lo que no he probado es a pararla desconectando la red a lo bruto. A lo mejor tu sabes mas al respecto. ¿Se podrí­a reanudar la descarga?. *-)

Saludos.
  • 0

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 01 mayo 2009 - 11:30

Muy buen aporte, cHackAll (y)

Una pregunta: ¿No se podrí­a hacer algo similar usando la API URLDownloadToFile apoyándose en IBindStatusCallback Interface en los métodos OnProgress y OnDataAvailable?

Con esa API se puede parar y reanudar la descarga y tenemos información de su estado. Lo que no he probado es a pararla desconectando la red a lo bruto. A lo mejor tu sabes mas al respecto. ¿Se podrí­a reanudar la descarga?. *-)

Saludos.


La prueba que hice amigo escafandra es terminar el programa desde el Administrador de Tareas, es decir, a la fuerza y al reiniciar el programa continuó desde el punto en que se "rompio" el proceso.

Por lo que pienso que los metodos que mencionas pueden ser utilizados en el programa de forma normal, es decir, si quiero pausar o reiniciar la descarga a través de un botón.

Salud OS
  • 0

#6 cHackAll

cHackAll

    Advanced Member

  • Administrador
  • 599 mensajes

Escrito 01 mayo 2009 - 03:30

...¿No se podrí­a hacer algo similar usando la API URLDownloadToFile apoyándose en IBindStatusCallback Interface en los métodos OnProgress y OnDataAvailable?...


Acabo de ser presentado con dicha API, lastimosamente la primera impresión que me dio esta relacionada con el nivel de programación que cada API presenta, mientras HttpSendRequest esta enviando una cabecera HTTP en un paquete al servidor y esta recibiendo los datos del mismo por un puerto con la API InternetReadFile, lo único que se puede ver por debajo es un par de sockets y algunas concatenaciones y eventos lo que nos da un mayor control de la descarga (Ej. descargar el mismo archivo por multiples secciones al mismo tiempo; como ya habí­a dicho un acelerador de descargas).

La API que me presentas trabaja con objetos COM que no solo engrosan el ejecutable, sino que tarde o temprano llegan a usar a WinInet y/o Winsock pero controlados por urlmon.

Ahora, talvez haya alguna confusión porque nunca he probado la API URLDownloadToFile, pero como comentaba egostar, con el ejemplo que les presento puede apagar el equipo en media descarga y reanudar la descarga una semana después (ej.).
  • 0

#7 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 04 mayo 2009 - 01:01

Acabo de ser presentado con dicha API, lastimosamente la primera impresión que me dio esta relacionada con el nivel de programación que cada API presenta.....


Ciertamente esa API está muy ligada al propio iExplorer y a su configuración por defecto

La API que me presentas trabaja con objetos COM que no solo engrosan el ejecutable, sino que tarde o temprano llegan a usar a WinInet y/o Winsock pero controlados por urlmon.


He analizado a fondo tu código y me ha parecido muy interesante. Me he permitido el lujo de traducirlo al C y de añadirle la posibilidad de usar un proxy y de identificarnos en él. Podrí­a añadirse la retroinformación del estado de descarga como apuntaba egostar:

Hay algunas adecuaciones que se me ocurren en este momento por ejemplo

  • Mostrar el avance de la descarga.
  • ......


También me he permitido, y espero que no te moleste cHackAll :$, publicar el código modificado en C en un hilo en el que he participado a cerca de este tema en CD.

Dicho esto publico mi humilde adaptación y ampliación en C, esperando no aburrir:



cpp
  1. // Esta es una modificación de la API HttpOpenRequest para permitir la autentificación en el Proxy si fuese necesario.
  2. HINTERNET HttpOpenRequest_Auth(
  3.         HINTERNET hConnect,
  4.         LPCTSTR lpszVerb,
  5.         LPCTSTR lpszObjectName,
  6.         LPCTSTR lpszVersion,
  7.         LPCTSTR lpszReferer,
  8.         LPCTSTR *lplpszAcceptTypes,
  9.         DWORD dwFlags,
  10.         DWORD_PTR dwContext,
  11.         LPCTSTR User,
  12.         LPCTSTR PassWord
  13.         )
  14. {
  15.   HINTERNET hRequest;
  16.   DWORD dwStatus, dwReserved;
  17.   DWORD dwStatusSize = sizeof(dwStatus);
  18.  
  19.   hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferer, lplpszAcceptTypes, dwFlags, dwContext);
  20.  
  21.   HttpSendRequest(hRequest, NULL, 0, NULL, 0);
  22.   HttpQueryInfo(hRequest, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize, &dwReserved);
  23.  
  24.   // Si es necesario me autentifico.
  25.   if(dwStatus == HTTP_STATUS_PROXY_AUTH_REQ || dwStatus == HTTP_STATUS_DENIED){
  26.       // Introducimos UserName
  27.       InternetSetOption(hRequest, INTERNET_OPTION_PROXY_USERNAME, (void*)User, lstrlen(User)+1);
  28.       // Introducimos el Password
  29.       InternetSetOption(hRequest, INTERNET_OPTION_PROXY_PASSWORD, (void*)PassWord, lstrlen(PassWord)+1);
  30.  
  31.   }else{
  32.       InternetCloseHandle(hRequest);
  33.       hRequest = HttpOpenRequest(hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferer, lplpszAcceptTypes, dwFlags, dwContext);
  34.   }
  35.  
  36.   return hRequest;
  37. }




La función FileDownLoad, basada en la función que publica cHackAll:


cpp
  1. // Esta es la función de descarga, basada en la original de cHackAll
  2. bool FileDownLoad(LPCTSTR URL, LPCTSTR Folder, LPCTSTR Proxy, LPCTSTR User, LPCTSTR PassWord)
  3. {
  4.   HINTERNET hInet, hConnect, hRequest;
  5.   HANDLE hFile;
  6.   DWORD Remainder = 0, Fail = 0, uBytes = 0, Size = 0, dwReserved = 0;
  7.  
  8.   BYTE Buffer[1024*64] = {0}; // 64 Kb.
  9.  
  10.   Remainder--;
  11.   Size = sizeof(Remainder);
  12.  
  13.   // Extraigo las partes de la URL
  14.   char c;
  15.   URL_COMPONENTS URL_C = {sizeof(URL_COMPONENTS)};
  16.   URL_C.lpszScheme = &c;
  17.   URL_C.dwSchemeLength = 1;
  18.   URL_C.lpszHostName = &c;
  19.   URL_C.lpszUrlPath = &c;
  20.   InternetCrackUrl(URL, strlen(URL), ICU_DECODE, &URL_C);
  21.   URL_C.lpszScheme = new char[URL_C.dwSchemeLength];
  22.   URL_C.lpszHostName = new char[URL_C.dwHostNameLength];
  23.   URL_C.lpszUrlPath = new char[URL_C.dwUrlPathLength];
  24.   InternetCrackUrl(URL, strlen(URL), ICU_DECODE, &URL_C);
  25.  
  26.   // Configuro el proxi al abrir un Handle Internet
  27.   if(Proxy)
  28.     hInet = InternetOpen("agent", INTERNET_OPEN_TYPE_PROXY, Proxy, NULL, 0);
  29.   else
  30.     hInet = InternetOpen("agent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
  31.  
  32.   hConnect = InternetConnect(hInet, URL_C.lpszHostName, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
  33.  
  34.   if(Folder) lstrcpy(Buffer, Folder);
  35.   hFile = CreateFile(lstrcat(Buffer, strrchr(URL, '//')+1), GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0); // will be closed with the process
  36.   Fail = 0;
  37.   do{
  38.     Sleep(Fail * 3000);
  39.     hRequest = HttpOpenRequest_Auth(hConnect, NULL, URL_C.lpszUrlPath+1, NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_DONT_CACHE, 0, User, PassWord);
  40.     lstrcat(itoa(SetFilePointer(hFile, 0, NULL, FILE_END), lstrcpy(Buffer, "range: bytes=")+13, 10), "-");
  41.     bool B = HttpAddRequestHeaders(hRequest, Buffer, lstrlen(Buffer), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
  42.     if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)){
  43.         HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &Remainder, &Size, &dwReserved);
  44.         while (Remainder > 0 && InternetQueryDataAvailable(hRequest, &Size, 0, 0) && Size > 0){
  45.           InternetReadFile(hRequest, Buffer, Size, &uBytes);
  46.           Remainder -= _lwrite((HFILE)hFile, Buffer, uBytes);
  47.           Fail = 0;
  48.         }
  49.     }
  50.     Fail++;
  51.     InternetCloseHandle(hRequest);
  52.   }while (Remainder != 0 && Fail != 3);
  53.  
  54.   InternetCloseHandle(hConnect);
  55.   InternetCloseHandle(hInet);
  56.   CloseHandle(hFile);
  57.  
  58.   delete URL_C.lpszScheme;
  59.   delete URL_C.lpszHostName;
  60.   delete URL_C.lpszUrlPath;
  61.  
  62.   return Remainder == 0;
  63. }



Aquí­ dejo el ejemplo de como usar la función FileDownLoad:



cpp
  1. bool D = FileDownLoad("http://catch22.net/sites/default/files/HexEdit121.zip", 0,
  2.   "proxy.........:80XX", "User", "Password");
  3. if(D)   
  4.   MessageBox(0, "Descarga completa.", "Completado", MB_ICONINFORMATION);
  5. else
  6.   MessageBox(0, "Descarga incompleta\n íntentalo mas tarde.", NULL,  MB_ICONEXCLAMATION);



Saludos.

PD: ACTUALIZO el código
He arreglado un error que sucedí­a con algunas URLs en la API HttpSendRequest, que provocaban el cuelgue de la función.
Aprovecho para cambiar el tipo de valor devuelto por FileDownLoad "true" si tubo éxito. Parece mas lógico que sea de ese modo.
Las URL deben tener delante la coletilla "http://" para que la API InternetCrackUrl las interprete correctamente. Otros sistemas de análisis de la URL pueden ser válidos, que cada cual escoja...
  • 0

#8 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 04 mayo 2009 - 01:21

Hola

Muy interesante amigo escafandra, se te ven las tablas en el manejo de C++ (y)

Salud OS

[off-topic]No debes de preocuparte por el asunto de publicar en ClubDelphi, en DelphiAccess no tenemos problema con ese tipo de situaciones y con ninguna otra y lo puedes ver desde el momento que hay administradores de DelphiAccess participando en ClubDelphi sin ningún problema.[/off-topic]
  • 0

#9 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 04 mayo 2009 - 01:39

Hola

Muy interesante amigo escafandra, se te ven las tablas en el manejo de C++ (y)


Muchar gracias por tu apreciación  :)

[off-topic]No debes de preocuparte por el asunto de publicar en ClubDelphi, en DelphiAccess no tenemos problema con ese tipo de situaciones y con ninguna otra y lo puedes ver desde el momento que hay administradores de DelphiAccess participando en ClubDelphi sin ningún problema.[/off-topic]

No me preocupa el tema de participar en ambos foros. Sólo querí­a ser respetuoso con cHackAll, al fin y al cabo la rutina original es de él  :)

Saludos.
  • 0

#10 cHackAll

cHackAll

    Advanced Member

  • Administrador
  • 599 mensajes

Escrito 04 mayo 2009 - 02:16

...Sólo querí­a ser respetuoso con cHackAll, al fin y al cabo la rutina original es de él  :)


Gracias por el respeto hacia mi, pero al codigo lo puedes ultrajar las veces que quieras y de las formas que te plazcan que para eso es que lo publico 8-|

PD; Gracias por la traduccion y por las aclaraciones acerca de URLDownloadToFile.

Salud
  • 0




IP.Board spam blocked by CleanTalk.