Ściąganie pliku z internetu

murdoc

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

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;

}

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

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

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

  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 <B>fSize</B> 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?

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

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.

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.

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

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

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

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

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

< delphi > </ delphi>