Sieć » Internet

Ściąganie pliku z internetu

Nie myślałem że to bedzie takie popularne ale skoro.....
Program służy do ściągania plików lub stron internetowych.
Część rzeczy można stąd pominąć a część należałoby zmienić na własny użytek, ale to by pogorszyło czytelność.


unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  wininet,
  StdCtrls, ComCtrls;
 
type
  TForm1 = class(TForm)
    Edit1: TEdit; 
    Label1: TLabel;
    Button1: TButton;
    Button2: TButton; 
    ProgressBar1: TProgressBar;
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
  stop: boolean; 
implementation
{$R *.DFM}
 
procedure TForm1.Button1Click(Sender: TObject);
var
  hInet, 
  hURL: HINTERNET;   
  fSize, 
  ReadLen, 
  RestartPos: DWORD;      
  fBuf: array[1..1024] of byte; 
  f: file;
  Header: string; 
begin
  RestartPos := 0;
  fSize := 0; 
  Button1.Enabled := false;
  Button2.Enabled := true;
  if FileExists('c:\123.tmp') then {tu należy podać nazwę pliku ściąganego-->>a więc funkcja odpowiednia  :)}
  begin
    AssignFile(f, 'c:\123.tmp');
    Reset(f, 1);
    RestartPos := FileSize(F); {gdy plik już istnieje na naszym komputerze  to ustawiamy pointer na końcu pliku żeby potem zacząć kopiowanie pliku od miejsca w którym skończyliśmy}
    Seek(F, FileSize(F));
  end
  else
  begin
    AssignFile(f, 'c:\123.tmp'); {jeśli zaczynamy kopiować plik po raz pierwszy to ustawiamy się na początku pliku}
    ReWrite(f, 1);
  end;
  hInet := InternetOpen('Mozilla',    {ble,ble->>patrz na samym dole}
    PRE_CONFIG_INTERNET_ACCESS,
    nil,
    nil,
    0);
 
  Header := 'Accept: */*';
  hURL := InternetOpenURL(hInet,
    PChar(Edit1.Text),
    pchar(Header),
    StrLen(pchar(Header)),
    0,
    0);
  if RestartPos > 0 then  {jeśli chcemy dokończyć kopiowanie pliku to wtedy ustaw pointer w pliku internetowym na miejscu w którym skończyliśmy ostatnio}
    InternetSetFilePointer(hURL,
      RestartPos,
      nil,
      0,
      0);
  InternetQueryDataAvailable(hURL, fSize, 0, 0);
  if RestartPos > 0 then 
  begin
    ProgressBar1.Min := 0;
    ProgressBar1.Max := fSize + RestartPos;
    ProgressBar1.Position := RestartPos;
  end
  else
  begin
    ProgressBar1.Min := 0;
    ProgressBar1.Max := fSize + RestartPos;
  end;
  while (ReadLen <> 0) and (stop = false) do
  begin
    InternetReadFile(hURL, @fBuf, SizeOf(fBuf), ReadLen);
    InternetQueryDataAvailable(hURL, fSize, 0, 0);
    ProgressBar1.Position := ProgressBar1.Max - fSize;
    BlockWrite(f, fBuf, ReadLen); 
    Application.ProcessMessages;
  end;
  stop := false;
  Button1.Enabled := true;
  Button2.Enabled := false;
  InternetCloseHandle(hURL); {zwalnianie poszczególnych uchwytów}
  InternetCloseHandle(hInet); 
  CloseFile(f); //zamknięcie pliku 
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  stop := false; 
  Button2.Enabled := false; 
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  stop := true; 
end;
 
end.


 HINTERNET-uchwyt potrzebny podczas pracy z protokołem http;
 fSize, ReadLen, RestartPos: DWORD-odpowiednio:wielkosc pliku,pozycja w pliku,pozycha w pliku gdy chcemy dokonczyć ściąganie pliku;
  fBuf: array[1..1024] of byte-tablica bufora do której kopiujemy dane;

Teraz opiszę dokładnie składnię internetopen(żeby każdy mógł se dostosować program ) która zwraca uchwyt i który wskazuje na wykorzystanie w aplikacji Wininet  :
hinet:=
nazwa_aplikacji- może to być dowolna wartość typu string;
rodzaj_żądanego_dostępu-(np. INTERNET_OPEN_TYPE_DIRECT,INTERNET_OPEN_TYPE_PRECONFIG,INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY,INTERNET_OPEN_TYPE_PROXY),
nazwa_servera_proxy_jesli_uzywa_sie_proxy-(w tym przypadku nie używamy servera proxy więć wartość ta wynosi nil(patrz "rodzaj żądanego dostępu)),
nazwy_hostów -gdy używamy połączeń proxy które nie powinny być rutowane( w naszym przypadku nie używamy proxy więc w tym miejscu wpisujemy nil),
opcjonalne_flagi- (INTERNET_FLAG_ASYNC,INTERNET_FLAG_FROM_CACHE,INTERNET_FLAG_OFFLINE) w naszym przypadku bez flag więc 0;

Składnia internetopenurl (uchwyt do zasobów) :
hurl:=
hinet=uchwyt otrzymany wcześniej (wskazujacy na obecnie otwartą sesję),
adres_internetowy_pliku-nic dodac nic ujać :),
nagłówek_wysyłany_do_servera_http,
długość_nagłówka,
flagi
context- wskazuje na określoną przez aplikację wartość która jest wysyłana  w przypadku odpowiedzi ( w tym przypadku 0)

 InternetQueryDataAvailable określa wielkość dostępnych danych.

InternetSetFilePointer( uchwyt_do_pliku,pozycja_w_pliku_od_której_zaczynamy_czytać_dane,
  zarezerwowane(musi być nil),początkowe_miejsce_od_którego_wystąpi_przesunięcie(FILE_BEGIN,FILE_current lub File_end),
  context(zarezerwowane)_i_musi_wynosić_0-ustawia pozycję w pliku który  chcemy skopiować,
 
Kopiowania plików nie będę tłumaczył (f1)

Jeżeli ktoś potrzebuje więcej opisów to radzę do internetu zajrzeć bo ja już więcej pisania nie zdzierżę.


P.S. Jak sa błedy to nie moja wina bo śpiący jestem

14 komentarzy

Centy819 2005-09-10 13:33

>> PiXel dnia 20-05-2005 17:20
>> Kurde wie ktoś dlaczego przy ściąganie pasek statusu wariuje?

Bo kurde program jest zle napisany ;-))))
Do wyznaczania wielkosci pliku uzyta zostala funkcja InternetQueryDataAvailable, ktora do tego nie sluzy. Zwraca ona cytujac MSDN "the amount of data available", czyli przyznany przez serwer kawalek pliku do jednorazowego odczytu. W zamian powinna byc zastosowana HttpQueryInfo. Oczywiscie funkcja dziala tylko dla plikow i stron statycznych. Jadro programu powinno wgladac mniej wiecej tak jak ponizej.

(sorki za C++, ale chcialem, uniknac mozliwych do popelnienia bledow przy tlumaczeniu na "pale", mam nadzieje ze koledzy sobie poradza ;-) )

Pozdrawiam Centy.

PS
Use MSDN Luke... ;-)

bool ReadFromInternet (AnsiString URL, AnsiString OutputFile, TProgressBar *progressBar)
{

    HINTERNET hSession, hURL;
    progressBar->Position = 0;
    bool result = true;              // wynik true - ok, false - blad
    bool fileSizeKnown = true;       // czy udalo sie nam uzyskac rozmiar pliku
   
    hSession = InternetOpen("Browser", INTERNET_OPEN_TYPE_PRECONFIG, "" ,"", 0);
    if (hSession==NULL)
        return false;
    hURL = InternetOpenUrl(hSession, URL.c_str(), "", 0, 0, 0);
    if (hURL==NULL)
    {
        InternetCloseHandle(hSession);
        return false;
    }

    unsigned long int fileSize;      // wynik funkcji HttpQueryInfo - tutaj rozmiar pliku
    unsigned long int varSize = sizeof fileSize; // rozmiar zmiennej fileSize
   
    if (!HttpQueryInfo (hURL, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &fileSize, &varSize, NULL))
    // HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER -
    // pytamy sie o dlugosc pliku, wynik ma byc typu DWord (defaultowo jest string)
    {
        if (GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND)
            fileSizeKnown=false; // nie mozna uzyskac informacji o dlugosci pliku, ale mozna go sciagnac
        else
        {
            InternetCloseHandle(hURL);
            InternetCloseHandle(hSession);
            return false;
        }
        // UWAGA - HttpQueryInfo zwraca false przy stronach dynamicznych lub HTML'owych
        // z niepoprawnym content'em np. brak headera - blad ERROR_HTTP_HEADER_NOT_FOUND
        // nie oznacza to ze pliku nie da sie sciagnac ;-)
    }

    if (fileSizeKnown)
        progressBar->Max = fileSize;
   
    unsigned long int readData;
    unsigned long int bufferSize;
    unsigned long int totalReadData = 0;
    int file = FileCreate(OutputFile);
   
    if (file==-1)  // nie udalo sie utworzyc pliku
    {
        InternetCloseHandle(hURL);
        InternetCloseHandle(hSession);
        return false;
    }
   
    do
    {
        if (!InternetQueryDataAvailable(hURL, &bufferSize, 0, 0))
        // sprawdzenie jak duzy kawalek pliku jest dostepny
        {  
            result = false;
            break;
        }
       
        if (bufferSize==0)  // sciagniety caly plik, konczymy sciaganie
            break;

        char *buffer = new char[bufferSize]; // alokacja tablicy mieszczacej dostepny kawalek pliku
       
        if (!InternetReadFile(hURL, buffer, bufferSize, &readData))
        {
            // nie udalo sie odczytac pliku
            delete [] buffer;
            result = false;
            break;
        }

        if (readData!=bufferSize)
        {
            // na wszelki wypadek sprawdzamy czy odczytano tyle ile kazano
            delete [] buffer;
            result = false;
            break;
        }
       
        if (FileWrite(file, buffer, readData)==-1)
        {
            delete [] buffer;
            result = false;
            break;
        }

        if (fileSizeKnown)
        {
            totalReadData += readData;
            progressBar->Position = totalReadData;
        }
        delete [] buffer;
    }    
    while (true);
   
    if (totalReadData != fileSize)
    // na wszelki wypadek sprawdzamy czy plik jest taki dlugi jaki powinien byc
        result=false;

    FileClose(file);
    InternetCloseHandle(hURL);
    InternetCloseHandle(hSession);
   
    return result;
}

Szczawik 2005-06-24 01:08

Tematyka podobna, tylko inna metoda: <url>http://4programmers.net/faq.php?id=8</url>

PiXeL2 2005-05-20 17:20

Kurde wie ktoś dlaczego przy ściąganie pasek statusu wariuje?

MrStroop 2005-04-02 11:17

hehe.. ten kodzik z malymi zmiankami .. tzn wywaleniem tego RestartPos to smiga jak pojeb..... swietny kod. Dziemki :D !!

zxc 2004-04-25 20:27

1) Mam pytanko do nagłówka (Header :) ).
Jeśli chciałbym w nim podać więcej paramertów to w jaki sposób? Oddzelać je średnikami/przecinkami/etc.
W jakiej formie je przekazywać nazwa:wartość/ nazwa=wartość

2) Grzebałem troszke w necie ale za dużo nie znalazłem. Podrzyć może jakieś konkretne, wartościowe linki.

3) zmienna fSize nie zawiera wielkości pliku. zazwyczaj jest to 0 albo kilka bajtów. Pliki które próbowałem nie są stronami php czy coś w tym stylu. próbowałem na wielu różnych plikach i stronach. Zawsze było to żle podawane. Gdzie szukać błędu?

Grzegorz 2003-10-30 14:59

Mam pytanko. Dlaczego jak zatrzymuję ściąganie pliku i wznawiam ponownie to program nie chce dokończyć owego ściągnięcia?

murdoc 2003-10-29 16:12

Komentarz Grześkowi::

Nie chciałem nikomu o tym mówić Grzesiek (tzn. o zerźnięciu kodu) ale skoro jestem zmuszony to zdradzę tajemnicę.....a więc było tak ::

Pewnego dnia gdy siedziałem na kiblu przeżywając wypróżnienie stulecia, w momencie kulminacyjnym przyszły do mnie krasnoludki i powiedziały tak: "Masz tu kod jak zastosować Winapi w Delphi, nikt tego kodu nie zna oprócz ciebie i nikt nie wie jak go używać jeszcze. Niech moc będzie z tobą".


W taki oto sposób  stałem się posiadaczem tego tajemniczego, nikomu nie znanemu kodu.

murdoc 2003-10-28 13:20

Poprawiłem co mogłem i starałem się wszystko okroić żeby było jak najbardziej czytelne.
P.S. Czasem progressbar przy większych plikach źle pokazuje mi pozycję....nie wiem czemu na razie.

Grzegorz 2003-10-25 17:56

Możemy liczyć na opis czy ten kod zerżnąłeś i sam nie wiesz, o co w tym kodzie chodzi!?

Waldi__17 2003-10-23 19:09

artykulik całkiem niezły... i przydatny przdewszystkim :)

korn 2003-10-23 14:11

wlasnie przydaly by sie jakies opiski, komentarze ;p ale to zalezy od autora

sablik 2003-10-23 06:20

A jak się wyśpisz to dodasz jakiś komentarz?

Marooned 2003-10-23 02:09

Tytuł jest świetny - mówi wszystko [rotfl] ;)

AndRew 2003-10-22 23:36

< delphi > </ delphi>