Ściąganie plików - dokończenie transferu

dziadek Prokop

W związku z tym że zauważyłem iż często padają pytania o dokończenie
transferu ściąganego pliku - a jestem w temacie ;)
Złożyłem do kupki te fragmenty kodów które można znależć w necie
i podaje poniżej kodzik który można bardziej rozbudować o ile znajdzie się
chętny.
Nie rozpisuje się na temat jego działania ponieważ staram się przy
instrukcjach opisywać ich znaczenia.
A to nawet dlatego że jestem starym sklerotykiem i po tygodniu nie pamiętam
co z czym sie je i jak używa :)


//dziadek Prokop ;)
UNIT Unit1;
interface
uses
 Wininet,
 Windows, Messages, SysUtils, Variants, Classes, Graphics,
 Controls, Forms, Dialogs, StdCtrls, ComCtrls, ExtCtrls;
type
  TForm1 = class(TForm)
    btnSciagnij: TButton;
    btnZatrzymaj: TButton;
    ProgressBar1: TProgressBar;
    Edit1: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Edit2: TEdit;

    PROCEDURE btnSciagnijClick(Sender: TObject);
    PROCEDURE btnZatrzymajClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

VAR
 Form1 :TForm1;
 Stop  :Boolean;
{***************************************************}
IMPLEMENTATION {$R *.dfm}

//Procedura ściąga lub "dociąga" plik z internetu...
PROCEDURE TForm1.btnSciagnijClick(Sender: TObject);
var
 adresURL,nazwaPliku            :String;
 plik                           :File;
 inOpen,inURL                   :HINTERNET;
 rozmiar,ileZnakow,rozmiarPliku :DWORD;
 dwBufLen, dwIndex, sciagnieto  :DWord;
 bufor :array[1..1024] of byte;
 buf   :Pointer;
Begin
 //ustawiamy dane...
 inOpen:= nil; inURL:= nil;
 rozmiarPliku:= 0;
 rozmiar:= 0;
 ProgressBar1.Position:= 0;
 //zmienna z adresem URL z którego ściągamy plik
 adresURL:= Edit1.Text;
 {zmienna z nazwą pliku jaka ma być po ściągnięciu
  - można wyciągnąć nazwe pliku z adresu URL}
 nazwaPliku:= Edit2.Text;

 //pobieramy rozmiar pliku jeżeli coś już ściągaliśmy...
 if FileExists(nazwaPliku) then
 begin
   AssignFile(plik, nazwaPliku);
   Reset(plik, 1);
   rozmiarPliku:= FileSize(plik); //pobieramy rozmiar pliku
   Seek(plik, FileSize(plik));    //ustawiamy skażnik na końcu pliku
 end
 else
 begin
   AssignFile(plik,nazwaPliku);
   ReWrite(plik, 1);
 end;

 Try
   //łączymy się z serwerem...
   Label1.Caption:='Łączenie...';
   Application.ProcessMessages;
   inOpen:= InternetOpen('Mozilla',PRE_CONFIG_INTERNET_ACCESS,nil,nil,0);
   inURL:= InternetOpenURL(inOpen,PChar(adresURL),'Accept: */*', StrLen('Accept: */*'),0,0);
   if not (inURL <> nil) then
   begin
     ShowMessage('Nie można uzyskać połączenia');
     Stop:= True;
     InternetCloseHandle(inURL);
     InternetCloseHandle(inOpen);
     CloseFile(plik);
     Exit;
   end;

   //pobieramy całkowity rozmiar pliku znajdującego sie na serwerze...
   dwIndex:= 0;
   dwBufLen:= 1024;
   GetMem(Buf, dwBufLen);
   HttpQueryInfo(inURL, HTTP_QUERY_CONTENT_LENGTH,buf, dwBufLen,dwIndex);
   //ustawiamy progresy (paski postępu)
   rozmiar:= StrToInt(StrPas(Buf));
   Caption:= 'Rozmiar pliku do ściągnięcia: '+ IntToStr(rozmiar);
   ProgressBar1.Max:= rozmiar;
   ProgressBar1.Position:= rozmiarPliku;

   { ustawiamy wskażnik pliku znajdującego się na serwerze, czyli
   ustalamy od jakiego miejsca w pliku rozpoczynamy pobieranie }
   if rozmiarPliku > 0 then InternetSetFilePointer(inURL,rozmiarPliku,nil,0,0);

   //pobieranie pliku z serwera...
   Label1.Caption:='Ściąganie pliku...';
   while (ileZnakow <> 0) and (Stop = false) do
   begin
     InternetReadFile(inURL, @bufor, SizeOf(bufor),ileZnakow);
     InternetQueryDataAvailable(inURL,rozmiar, 0, 0);
     BlockWrite(plik, bufor,ileZnakow);
     sciagnieto:= FileSize(plik);
     Label2.Caption:='Ściągnięto '+ IntToStr(sciagnieto);
     ProgressBar1.Position:= sciagnieto;
     Application.ProcessMessages;
   end;

 except
   ShowMessage('Wystąpił błąd podczas ściągania pliku !');
   Stop:= True;
   InternetCloseHandle(inURL);
   InternetCloseHandle(inOpen);
   CloseFile(plik);
   Exit;
 end;

 Stop:= False;
 InternetCloseHandle(inURL);
 InternetCloseHandle(inOpen);
 CloseFile(plik);
 if ProgressBar1.Position = ProgressBar1.Max then
    Label1.Caption:= 'Plik został ściągnięty'
    else Label1.Caption:= 'Zatrzymane ściąganie pliku';
 Beep;
End;

//zatrzymanie pobierania...
PROCEDURE TForm1.btnZatrzymajClick(Sender: TObject);
begin
 Stop:= true;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
 Edit1.Text:= 'http://www.almdev.com/prods/baseskins_compress.zip';
 Edit2.Text:= 'ściągnięty plik.zip';
end;

END.

12 komentarzy

Wiem że wątek stary, ale może ktoś ma pojęcie jak sprawdzić czy plik wogóle istnieje ? Bazując na tym artykule zrobiłem kod który sciaga plik , ale ściaga zawsze, nawet jak link jest zły, to ściaga html'a o tym ze nie ma pliku...Prosze o odpowiedź

Niestety kod nie działa jak należy. Przykład: przerwiemy pobieranie pliku, wyłączymy program a następnie załączymy go ponownie i zechcemy wznowić pobieranie. Program się zawiesi (w tle pobiera plik od nowa) i ruszy dopiero wtedy, gdy zacznie sciągać tego co jeszcze nie pobrał. Bezsens bo równie dobrze można pobrać od nowa. Implementacja ContentRange też mi nie działa.

Dlaczego Aplikacja zawiesza sie kiedy przerwie sie pobieranie a potem sie je wznowi??

Czy ktoś wie skąd pobrać moduł MSHtml ?
Piszę przeglądarkę i nie umiem go znaleść.

Myślę, że aby wyświetlić aktualny transfer ściąganego pliku należy:

  1. Dać zmienną zapisującą ilość ściągniętych danych: Dtransfer:=Dtransfer+IleZnakow;
  2. Utworzyć dodatkowy wątek aplikacji z Ttimer'em ustawionym na 1 sekundę (ze zmienną DLatest), który co sekundę wykonuje następujące czynności:
    a) wyświetla rzeczywisty transefer będący różnicą: Rzeczywisty_Transfer=DTransfer - DLatest;
    b) kopiuje wartość aktualnej zmiennej Transfer: DLatest:=DTransfer;

Należy pamiętać, aby DTransfer i DLatest były typu Double, gdyż Int64 może okazać się za mały przy ściąganiu dużych plików, czego konsekwencją może być przekręcenie licznika i wykazanie niebotycznego transferu :-)

Ciekawą kwestią jest ściąganie pliku z możliwie maksymalnym transferem. Podejrzewam, że trzeba odpowiednio dostosować bufor, który w omawianym przykładzie wynosi 1024 bajty, teoretycznie im większy bufor tym szybszy transfer pod warunkiem, że nie przekracza on znacząco wielkości ścągniętej porcji danych, bo wtedy zapisanie bufor'a(u) na dysk fizyczny wstrzymywane jest do czasu wypełnienie całej tablicy bajtami. Większy bufor dłużej się zapisuje i zamiast przechwytywać kolejną porcję danych tracony jest czas na zapis dużego bufora. Znowuż zbyt mały bufor wymaga więcej obrótów pętli i naturalnie znacznie więcej czasu. Reasumując lepiej więcej niż mniej, ale nie za dużo :-)

Czy wie ktoś jak w tym przypadku wysietlić szybkość transferu podczas sciągania?

Przyda mi się :)

spoko art. tego szukałem....:)

Dobre, mysle ze sie moze bardzo przydac :)

super rzecz! tylko nazwij archiwum jakoś inaczej

tylko, że NIC NIE DZIAŁA !!!!!!!!!!!!!!

wow to lubie, konkrety...