Pobieranie dużego pliku z internetu z obsługą SSL

0

Witam serdecznie,
Szukam rozwiązania palącego problemu. Próbuje napisać program, który potrafi pobrać pliki z internetu. Niby proste.
Używam Delphi z Indy 10 (Berlin 10.1 free) oraz idHTTP.
Pobranie pliku z serwera bez szyfrowania jest proste i działa doskonale.
Problemem jest jednak to, że pliki mogą ważyć ponad 100MB. Dlatego zdecydowałem się umieścić plik na dysku internetowym (powiedzmy One Drive, mam tutaj >1TB). Ale może to być dowolny serwer HTTPS (Google Drive, DropBox, Mega, etc).
Mogę tam wygenerować linka i udostępnić go publicznie. Jest OK. Pobierając go przeglądarką wszystko śmiga...

Problem:
Jak taki plik pobrać w aplikacji napisanej w Delphi? Kombinowałem z Indy i SSL (biblioteki ssl są w katalogu)
Nie działa. Nie ma błędów. Nie ma też reakcji... Problemem chyba są liczne przekierowania, nie wiem.

 

procedure DownloadFile;
var
  GetData : TFileStream;
  sURL: string;
begin
  SERVER_UPDATE_URL := 'https://1drv.ms/u/s!AhCBb-CkOnQpixi2Vmq9RoLxqwXQ';  // <-- mój przykładowy plik (instalator dla winamp, ale to nieważne co to jest)

  try
    GetData := TFileStream.Create(TEMP_DIR + '\' + 'NAZWA_PLIKU.exe', fmOpenWrite or fmCreate); //< -- plik zapisywany w TEMP
    try
        HTTP.Get(SERVER_UPDATE_URL, GetData);
    finally
      GetData.Free;
    end;
  except
    on E: EIdOSSLCouldNotLoadSSLLibrary do
         ShowMessage(E.message);
    on E: EIdHTTPProtocolException do
         ShowMessage(E.message);
    on E: EIdConnClosedGracefully do
         ShowMessage(E.message);
    on E: EIdSocketError do
         ShowMessage(E.message);
    on E: EIdException do
         ShowMessage(E.message);
    on E: Exception do
      ShowMessage(E.message);
  end;

// W katalogu mam bibliteki SSL (ssleay32.dll, libeay32.dll).
// Na formie idHTTP, IdSSLIOHandlerSocketOpenSSL, IdCookieManager

Reasumując, czy ktoś z was ma wiedzę jak pobrać plik z tego typu serwerów (niech będzie OneDrive) programowo?
Program ma połączyć się z dyskiem OneDrive (Google, mega etc), pobrać plik i wyświetlić progress (postęp pobierania, ilość pobranych, średnia szybkość).
Jestem w stanie nawet zapłacić drobną kwotę za rozwiązanie lub pomoc...

-Pawel

0

Tak, próbowałem. Nie działa :(
Redirects oczywiście jest na True.
Stronę, która podałeś sprawdzałem (bo co oczywiste pół dnia spędziłem na próbach, googlu i bóg wie czym :P)

1

Słabo szukałeś.
Na PRIV masz pomocny projekt.

Chociaż moim zdaniem kluczowe jest podanie odpowiedniego odnośnika, dla Twojego przykładu to będzie:
https://onedrive.live.com/download.aspx?cid=29743AA4E06F8110&authKey=%21ALZWar1GgvGrBdA&resid=29743AA4E06F8110%211432&ithint=%2Eexe

pozdrawiam

0

Rzeczywiście działa.
Ale:

  1. Jakim cudem wygenerowałeś ten adres z mojego, który zapodał mi OneDrive?
  2. Dlaczego idHTTP nie wyświetla żadnych danych w HTTPStatus, HTTPWork, HTTPWorkBegin, HTTPWorkEnd?

Proszę o wyjaśnienia dla powyższych.
Dzięki,
Pozdrawiam

0
  1. Sniffer HTTP
  2. Powinno się coś dziać. Pokaż kod.
0

Mój kod jest de facto taki jak twój. Tylko, że ja mam poustawiane właściwości w komponencie, a nie kodem.

procedure TDownload.DownloadFile;
var
  Buffer: TFileStream;
  GetData : TFileStream;
begin
   //SERVER_UPDATE_URL := 'https://1drv.ms/u/s!AhCBb-CkOnQpixi2Vmq9RoLxqwXQ';
   //SERVER_UPDATE_URL := 'https://onedrive.live.com/?authkey=%21ALZWar1GgvGrBdA&cid=29743AA4E06F8110&id=29743AA4E06F8110%211432&parId=29743AA4E06F8110%211426&action=locate';
   
   
   SERVER_UPDATE_URL := 'https://onedrive.live.com/download.aspx?cid=29743AA4E06F8110&authKey=%21ALZWar1GgvGrBdA&resid=29743AA4E06F8110%211432&ithint=%2Eexe';

   // Show Download Page
   M_Download.Clear;
   M_Download.Lines.Add('');
   M_Download.Lines.Add('Starting to download UFM update file.');
   M_Download.Lines.Add('   ' + 'Update link: ' + SERVER_UPDATE_URL);
   M_Download.Lines.Add('');


  try
    GetData := TFileStream.Create(TEMP_DIR + '\' +  SERVER_UPDATE_NAME, fmOpenWrite or fmCreate);
    try
        HTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 5.1; rv:2.0b8) Gecko/20100101 Firefox/4.0b8';
       (*
To mam ustawione w komponencie
    IOHandler := IdSSLIOHandlerSocketOpenSSL1;
    AllowCookies := True;
    HandleRedirects := True;
    RedirectMaximum := 35;
    HTTPOptions := [hoForceEncodeParams];
    CookieManager := IdCookieManager1;
*)
        HTTP.Get(SERVER_UPDATE_URL, GetData);
        HTTP.Disconnect;
    finally
      GetData.Free;
    end;
  except
    on E: EIdOSSLCouldNotLoadSSLLibrary do
         ShowMessage(E.message);
    on E: EIdHTTPProtocolException do
         ShowMessage(E.message);
    on E: EIdConnClosedGracefully do
         ShowMessage(E.message);
    on E: EIdSocketError do
         ShowMessage(E.message);
    on E: EIdException do
         ShowMessage(E.message);
    on E: Exception do
      ShowMessage(E.message);
  end;
end;

procedure TDownload.HTTPStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
begin
   M_Download.Lines.Add(AStatusText); // <-- tu powinno się sporo dziać (to log w TMemo)
end;

procedure TDownload.HTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
  PercentDone: Double;
  ElapsedMS: LongWord;
  BytesTransferred: Int64;
  BytesPerSec: Int64;
begin
   Application.ProcessMessages;

   if AWorkMode <> wmRead then Exit;

   ElapsedMS := GetTickDiff64(LastTicks, Ticks64);
   if ElapsedMS = 0 then ElapsedMS := 1; // avoid EDivByZero error

   if TotalBytes > 0 then
      begin
          PercentDone := (AWorkCount / TotalBytes) * 100.0;
      end
   else
      begin
          PercentDone := 0.0;
      end;

  BytesTransferred := AWorkCount - LastWorkCount;
  BytesPerSec := (BytesTransferred * 1000) div ElapsedMS;

  LastWorkCount := AWorkCount;
  LastTicks := Ticks64;

   M_Download.Lines.Add('Pobrano: ' + GetFormattedSize(AWorkCount, '3', True) + ' z ' +  GetFormattedSize(UFM_FILE_SIZE, '3', False) + '. Szybkość: ' + IntToStr(BytesPerSec) + 'b/s' + ' (' + FormatFloat('0.#', PercentDone) + '%' + ')');
   PB_Download.Position := AWorkCount;
end;

procedure TDownload.HTTPWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
begin
  if AWorkMode <> wmRead then Exit;

  TotalBytes := AWorkCountMax;
  LastWorkCount := 0;
  LastTicks := Ticks64;

  PB_Download.Max      := AWorkCountMax;
  PB_Download.Position := 0;
end;

procedure TDownload.HTTPWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  if AWorkMode <> wmRead then Exit;

   // Completed
   if UFM_SERVER_UPDATE_ERROR = False then
      begin
         M_Download.Lines.Add('Download completed successfully!');

         // Read Data
		 // Do something with downloaded data
      end;
end;



procedure TDownload.HTTPConnected(Sender: TObject);
begin
   //
end;

procedure TDownload.HTTPDisconnected(Sender: TObject);
begin
   //
end;

A co do linka... hmm, sniffer. Jakaś lepsza metoda do ogarnięcia prawidłowego linka? (bo plik będzie się zmieniał i głupio by było za każdym razem mordować się z szukaniem prawidłowego linku... metoda musi być skuteczna i prosta :) )

0

@lampasss
Dziękuję za pomoc.
Już znalazłem rozwiązanie problemu. Powyższy kod jest OK!
Wszystko działa.
Nie miałem odpowiedzi HTTP, ponieważ jakimś cudem procedury idHTTP straciły powiązanie z komponentem (chyba przy kopiowaniu, a nie zwróciłem na to uwagi - przez to nic się nie wykonywało w np. HTTPStatus :)).
-Pawel

0

Podbijam temat. Czy ktoś z szanownych forumowiczów może pokazać przykład pobierania pliku z internetu (zwykły plik tekstowy).
Dotychczas używałem INDY oraz bibliotek SSL (libeay32.dll, ssleay32.dll). Ale, ciężko znaleźć najnowsze binarki SSL dla INDY.
Dlatego postanowiłem zmienić silnik na ICS (http://www.overbyte.eu/frame_index.html?redirTo=/products/ics.html). Ale to potężny pakiet wielu komponentów.

Ktoś podpowie czy ICS pozwala na taką prostą operację jak pobranie pliku? Może jest do tego jakieś demo?
Jak wy to robicie? Plik, który pobieram jest prostym plikiem INI, który zawiera informacje o wersji programu (pobiera go program aktualizujący).
-Pawel

1

Ja bym się nie męczył. Wrzuciłbym wersję wget dla Windows do folderu programu i za jego pomocą pobierałbym pliki.
W rozwiązaniach dla Delphi prawie zawsze będziesz krok w tył jeśli chodzi o zabezpieczenia, co nie jest dziwne bo społeczność nie jest zbyt duża.
wget jest potwornie popularny w linuxach i rozwijany jest bardzo mocno. Tutaj prawie nie ma szans na problemy z ssl-em czy innymi zabezpieczeniami. Dodatkowo stabilność jest nieprawdopodobna.
Jedyny problem to przedstawienie progresu pobierania plików. Ale to też można zrobić na kilka sposobów.
Poza tym będzie to działać całkowicie w tle więc możesz spokojnie zwolnić swoje okno.

Jedyny problem to wywołanie wget-a przez shellexecute i jak już zapewne zauważyłeś w innym wątku na tym forum sprawia to malutkie problemy z antywirusami którym taki stan rzeczy się nie do końca podoba.

mały OT
wget jest już w PowerShell Windowsa, trzeba by tylko poszukać jak go użyć i nawet nie trzeba pobierać specjalnej wersji

0

indy jest rozwijane caly czas a i dlki sa nowe, ale akurat nie mam pod reka linka, pewnie zaraz ktos wrzuci np. @kAzek

0

@Pepe: słuchaj, jak potrzebujesz tylko sprawdzić aktualną wersję to może załatw to inaczej, np. serwerem REST? Bardzo przyjemnie się to oprogramowuje i nieźle działa.

2

OpenSSL 1.0.2u jest ostatnią wersją z gałęzi 1.0.x - ta gałąź nie jest już wspierana od 2020-01-01.
Natomiast nowsze biblioteki w wersji 1.1.X nie są wspierane przez Indy.

1

Osobiście nie sprawdzałem ale jeżeli chodzi o obsługę TLS 1.3 w Indy 10 to można się przyjrzeć:
https://github.com/tothpaul/Delphi/tree/master/Indy.SChannel/
Na razie ostro zajęty jeszcze dobre kilka dni więc raczej nie znajdę czasu na zabawę i testy tego.

1 użytkowników online, w tym zalogowanych: 0, gości: 1