artykuły

Delphi - Pobieranie plików z Internetu

1:08
sob, 25 styczeń 2003
Artykuł pokazuje sposób ściągać pliki z Internetu z poziomu kodu naszego programu. W artykule wykorzystujemy bibliotekę UrlMon, która jest standardowo instalowana wraz z Delphi.

Wstęp

Witam wszystkich. Dzisiaj opisywał będę ciekawą funkcję na którą ostatnio się natknąłem. Jest niąUrlDownloadToFile. Pozwala ona na pobranie określonego pliku z sieci. Na koniec wykorzystamy zdobytą wiedzę w praktyce, spróbujemy napisać prosty program typudownload-manager. Na wszystko jednak będzie pora później. Teraz omówię samą funkcję.

Budowa "UrlDownloadToFile"

Budowa przedstawionej wyżej funkcji prezentuje się następująco:
UrlDownloadToFile(Caller: IInterface, URL: PChar, FileName: PChar, Reserved: Cardinal, StatusCB: IBindStatusCallBack); URL - nazwa pliku który chcielibyśmy pobrać
FileName - nazwa pliku pod którą plik pobrany zostanie zapisany na lokalnym dysku.
Reszta parametrów nie będzie nam potrzebna, jednak omówię je po krótce: Reserved - zarezerwowany dla przyszłych wersji tej funkcji - w obecnych wersjach zalecane jest ustawianie go na 0
StatusCB - bardzo ciekawy parametr pozwalający nam odbierać informacje dotyczące transferu (mamy do dyspozycji zdarzenia takie jak np. OnProgress)

Funkcja ta nie znajduje się w bibliotece dołączonej do Delphiego (jakSystem,MathczyRegistry) lecz jest ona zawarta w jednej z bibliotek znajdujących się w systemie. Mówiąc nieco dokładniej, jest to biblioteka"UrlMon.dll"znajdująca się w katalogu System w folderze z Winodwsem. Zwykle jest to C:\Windows\System\UrlMon.dll. Aby z niej skorzystać wystarczy dodać do listyusesnazwę tej biblioteki. Delphi zrobi resztę za nas. W naszym wypadku, po przecinku, do listyusesdodajemy słówkoUrlMon. To wszystko, możemy już korzystać z nowej biblioteki. W tej bibliotece znajdują się funkcję, które jeszcze przez długi okres waszego życia pozostaną w cieniu. Dobrze, zatem możemy teraz wypróbować tą jedną jedyną znaną nam funkcję. Jak to zrobić? Zaraz pokażę.

UrlMon.UrlDownloadToFile(nil, http://d.g3.pl/at/archiwa/at24.exe','C:\Dokumenty\Prenumeraty\@t24.exe',0,nil);

Powyższy przykład zawiera instrukcję, która dla programu w obecnym zapisie znaczyła będzie tyle co: "pobierz plik http://d.g3.pl/at/archiwa/at24.exe i zapisz go jako C:\Dokumenty\Prenumeraty\@t24.exe". Słówkonil, w Delphi, oznacza dla kompilatora, czyli programu, który "zmienia" wszystkie instrukcje napisane przez programistę w język zrozumiały tylko dla procesora tzw. kod maszynowy tyle co pustka, czyli prościej mówiąc jest to "nic". Nie wskazuje ono na żaden element. Możemy go używać przy różnych parametrach, ale uwaga, nie we wszystkich! Czasami jeśli kompilator wskazuje błąd w takim miejscu, wystarczy jedynie zamienićnilna inny pusty odnośnik, zwykle jest to np.0. Ale i tutaj zero nie zawsze pomoże. W każdym razie należy zapamiętać, żeniloznacza dla kompilatora "pustkę".

Piszemy program

Aby napisać (bardzo) prosty download-manager, trzeba już mieć nieco wiedzy z tej dziedziny. Tak, więc uważam, że znacie pętle, instrukcje warunkowe, budowę procedur i funkcji i inne szpeja, bo bez tego ani rusz.

Kosmetyka

Otwieramy Delphiego. Na nowej formie umieszczamy komponentListBoxznajdujący się na palecieStandard. Następnie, pod nim, trzy przyciski, klikając na odpowiednią ikonkę na palecie, następnie na formę w miejscu gdzie ma zostać on umieszczony. Zmniejszamy formularz do takich rozmiarów, aby wszystko mieściło się na nim, a zarazem dbamy o jego estetyczny wygląd.
Następnym krokiem jest nadanie nazw poszczególnym elementom.
Zaznaczamy formularz i w inspektorze obiektów (Obejct Inspector - F11) odszukujemy poleName, gdzie wpisujemy "fmMainForm", następnie poleCaption, którego wartość zmieniamy na (i tu dowolnie) np. "Pobieranie plików - przykład". Następnie zaznaczamy kontrolkęListBox(pojedynczym kliknięciem of courz ;), a w inspektorze obiektów poleNamewypełniamy następująco: "ListaPlikow". Jeśli macie zamiar skopiować poniższy kod, ważne jest abyście zważali uwagę na nazwy które teraz dyktuje. Są to nazwy elementów, jakich użyłem. W takim wypadku nawet najmniejsze odstępstwo od pierwotnej nazwy wywoła błąd. Jednak mam nadzieję, że sami spróbujecie uporać się z kodem i w całości go zrozumieć.
Przyszła kolej na przyciski. Zaznaczamy przycisk"Button1"i w poluCaption(w inpektorze obiektów) wpisujemy "Dodaj adres". Przyciskowi"Button2"w poluCaptionnadajemy etykietę"Usuń adres", a"Button3"podpisujemy"Pobierz wszystkie". Od tej chwili na naszych przyciskach będą widniały powyższe etykiety (Caption).

Kod źródłowy - czyli to, co tygryski lubią najbardziej ;)

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, UrlMon, OleCtnrs, DdeMan; type TfmMainForm = class(TForm) Button1: TButton; ListaPlikow: TListBox; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public procedure Pobierz(url: string); // Deklaracja procedury pobierającej pliki function WyluskajNazwePlikuZAdresuWWW(url: string) : string; // Funkcja wyłuskująca z adresu WWW nazwę pliku function Sprawdz(url: string): Boolean; // Deklaracja funkcji sprawdzającej poprawność adresu end; var fmMainForm: TfmMainForm; implementation {$R *.dfm} // Funkcja sprawdzająca poprawność adresu function TfmMainForm.Sprawdz(url: string): Boolean; var adres1, adres2: string; // zadeklarowanie łańcucha znaków begin // Dwie zmienne, jedna z nich - jeśli poprawny jest adres podany przez użytkownika // przyjmuje wartość - albo http://, albo ftp:// adres1 := Copy(url,1,7); // Powinno zwrócić http:// adres2 := Copy(url,1,6); // Powinno zwrócić ftp:// // Jeśli jedna ze zmiennych zawiera poprawny początek ('http://' lub 'ftp://') to... If (adres1 = 'http://') or (adres2 = 'ftp://') Then // zwróć True(Prawda) || Jeśli nie, zwróć False (Fałsz) Result := True else Result := False; end; // Funkcja wyłuskująca z adresu WWW nazwę pliku function TfmMainForm.WyluskajNazwePlikuZAdresuWWW(url: string) : string; var i : integer; begin // powiedzmy, że mamy przykładowy adres http://www.onet.pl/plik.exe // szukanie ostatniego slasha w adresie (czyli pierwszego od tyłu) for i:=Length(url) DownTo 1 Do if url[i] = '/' Then begin // Kopiujemy część adresu od znalezionego slasha (jeden znak po nim), aż do końca ciągu // Funkcja Trim obcina nam ewentualne spacje znajdujące się po bokach łańcucha (na jego początku i końcu) Result := Trim(Copy(url,i+1,Length(url)-i)); // Przerywamy wykonywanie pętli Break; end; end; // Funkcja pobierająca plik procedure TfmMainForm.Pobierz(url: string); var plik: string; // Deklaracja zmiennej tekstowej plik begin // Wyciągnięcie z adresu url nazwy samego pliku, spełnia to funkcja // "WyluskajNazwePlikuZAdresuWWW()" plik := WyluskajNazwePlikuZAdresuWWW(url); // Jeśli pomimo wszystkich naszych wysiłków wyciągnięta nazwa jest pusta (odpowiada // to np. adresowi www.onet.pl/podstrona/ if plik = '' Then begin // przypisywanie domyślnej nazwy pliku np. domyslna_nazwa.html ;) // UWAGA! Możliwość nadpisania już istniejącego pliku plik := 'domyslna_nazwa.html'; end; // następnie dodanie litery dysku. W efekcie otrzymujemy: // C:\ + plik.exe = C:\plik.exe. plik := 'C:\'+plik; // Funkcja o której była mowa, to ona odpowiada za pobranie naszego pliku UrlDownloadToFile(nil,PChar(url),PChar(plik),0,nil) end; // Procedura wywoływana po naciśnięciu przycisku Button1 ("Dodaj adres") procedure TfmMainForm.Button1Click(Sender: TObject); var PlikDoPobrania: String; begin // Wyświetl okienko do wpisywania adresu, to co poda użytkownika zapisz w zmiennej "PlikDoPobrania". PlikDoPobrania := InputBox('Wprowadź adres...','Poniżej wprowadź adres pliku, który ma zostać pobrany',''); // Wywołaj funkcję Sprawdz(), sprawdzi ona poprawność adresu podanego przez użytkownika If Sprawdz(PlikDoPobrania) Then Begin // Jeśli adres jest poprawny to... // Dodaj adres, który wpisał użytkownik do listy ListaPlikow.Items.Add(PlikDoPobrania); // Wywołaj procedure Pobierz, która pobierze ów plik. // Zastosowane tutaj tzw. rzutowanie typów zostanie omówione w dalszej części artykułu Pobierz(PlikDoPobrania); end else ShowMessage('Niepoprawny adres!'); end; // Procedura wywoływana po naciśnięciu przycisku Button2 ("Usuń adres") procedure TfmMainForm.Button2Click(Sender: TObject); begin // Usuń pozycję o indeksie, który posiada zaznaczona pozycja, czyli // usuń zaznaczenie. ListaPlikow.Items.Delete(ListaPlikow.ItemIndex); end; // Procedura wywoływana po naciśnięciu przycisku Button3 ("Rozpocznij pobieranie") procedure TfmMainForm.Button3Click(Sender: TObject); var i: integer; begin // Pętla For...do { liczy od 0 do liczby jaka charakteryzuje ilość pozycji na liście ListaPlikow Prościej mówiąc ta pentelka patrzy na pierwszą pozycję(adres) listy, zaczyna go pobierać, następnie drugi adres i znów rozpoczyna jego pobieranie, dochodzi do trzeciego adresu i pobiera go - i tak do końca listy.} For i:=0 To ListaPlikow.Items.Count-1 Do Begin // Wywołujemy procedurę odpowiadającą za pobieranie plików. Pobierz(ListaPlikow.Items[i]); end; end; end.

Opis

Myślę, że kod powinien być dla Was zrozumiały. Każdą instrukcję obarczyłem komentarzem. Jedną rzecz pozostawiłem do szerszego opisu. Tak, chodzi o rzutowanie typów. Jest to bardzo ważna umiejętność. Wytłumaczyć to można w sposób iż jest to jakby oszukanie kompilatora. Nie we wszystkich przypadkach możemy uciec się do zastosowania konwersji typów, np.StrPas(), czyStrPCopy(). Spójrzcie na poniższy przykład.
Przypuśćmy, że za pomocą komunikatu (MessageBox) chcemy wyświetlić zmienną typuString. Kompilator nie zgodzi się na taki układ, generując błąd o niezgodności typów. Błąd niezgodności typów pojawia się nam wówczas gdy próbujemy np. jako parametr jakiejś funkcji próbujemy wstawić zmienną o typie, który nie pasuje do typu tegoż parametru. Najlepiej zrozumiecie to na przykładzie.

var informacja: String; begin informacja := 'Cześć!'; Application.MessageBox(informacja,'Komunikat',MB_OK);

Pierwszy parametr funkcjiMessageBoxto tekst jaki ma wyświetlić się w komunikacie. Tekst ten musi być typuPChar, my mamy zmienną "informacja" typuString.PChariString, chodź oba są typami tekstowymi - różnią się od siebie budową i ilością zajmowanej pamięci, dlatego nie są ze sobą kompatybilne. Możemy łatwo przekonwertować wygodny w użytkowaniu typ String na typ PCHar korzystając z funkcji PChar()
Z tego co napisałem wynika, że ostatnia linia powinna wyglądać następująco:

Application.MessageBox(PChar(informacja),'Komunikat',MB_OK);

Tym sposobem przekonwertowaliśmy zmienną typuStringna typPchar. Teraz program się skompiluje i uruchomi. Powodzenia!


Włączone do kontaktu, lepiej działa (prawo Sattingera)

Biorąc powyższy cytat pod uwagę, uruchamiamy napisany wcześniej program (F9 lub Project\Run). Klikamy przycisk"Dodaj adres"i wpisujemy adres do pliku, który chcielibyśmy pobrać z sieci, np. http://d.g3.pl/at/archiwa/at24.exe', a następnie klikamy na przycisk OK. Jeśli wszystko poszło zgodnie z planem - system będzie próbował teraz nawiązać połączenie z siecią i wykonać zadanie (w naszym wypadku pobrać magazyn @t).

Odpowiedzi na listy czytelników

Zastosowałem tę procedurę w swoim programie (Win 7, Delphi Ent 5). (...) Plik (na serwerze - dop. Lukas) jest przygotowany po raz pierwszy rano, a następnie uzupełniany jest o dodawane rekordy. (...) Niestety, zaobserwowałem następujące zjawisko: Jeśli plik jest ściągany po raz pierwszy w danym dniu, wszystko jest OK. Przy następnym ściągnięciu plik pobrany później nie różni się od ściągniętego po raz pierwszy w danym dniu, nawet jeśli plik istniejący na serwerze jest uzupełniony o następne rekordy.
(...)
Pozdrawiam - Henryk

Odpowiedź na list Henryka

Drogi Henryku, odpowiada za to mechanizm pamięci tymczasowej Cache w Windows. Jest to więc niedogodność związana z systemem operacyjnym. Aby ją usunąć i ściągać zawsze najnowszą wersję danego pliku, musimy wyczyścić cache dla danego adresu poleceniem DeleteUrlCacheEntry, z biblioteki WinInet (należy ją wraz z UrlMon dodać do listy "Uses").
Przykładowe użycie: procedure TForm1.Button1Click(Sender: TObject); var url: PChar; begin url := http://example.com/plik.txt'; DeleteUrlCacheEntry(url); UrlMon.URLDownloadToFile(nil, url,'C:\plik.txt', 0, nil); end;

Zakończenie

Funkcja bardzo ciekawa - czyż nie? ;) Myślę, że muszę poświęcić jej nieco więcej czasu i rozpracować bibliotekę"UrlMon.dll". Zapomniałem was poinformować o istotnej sprawie. Mianowicie, jeżeli macie zainstalowany u siebie GetRight, czy inne badziewie może on przejąć od systemu pobieranie pliku, więc jeśli nie zobaczysz systemowego okienka - zobaczysz z pewnością okienko GetRighta czy innego programu wspomagającego. Chciałbym dodać iż tutaj znajduje kod źródłowy programu który pisaliśmy w tym artykule.
Dzięki wszystkim za uwagę!

12345
Delphi - Pobieranie plików z Internetu Autor opinii: Czytelnicy, data przesłania: 5

Podobne artykuly:

Skomentuj

Aby zamieścić komentarz, proszę włączyć JavaScript - niestety roboty spamujące dają mi niezmiernie popalić.






Komentarze czytelników

    • TomRiddle
    • sob, 7 maj 2011, 15:01
    • Dobrym przykładem rzutowania może być:

      var Ch : Char;
      B : Byte;
      begin
      Ch := 'a';
      B := Byte(Ch);
      end;
    • eSeLU
    • sob, 26 wrzesień 2009, 20:12
    • Application.MessageBox(PChar(informacja),'Komunikat',MB_OK);


      Wmówiliśmy kompilatorowi iż zmienna "informacja" jest typu PChar, chodź w rzeczywistości jest ona zmienną typu String.

      Pragnę powiedzeć ze to nie jest wmowienie czy oszukanie czy jakas inna magiczna rzecz. jest to poprostu funkcja zwracająca wartość w postaci Pchar, poprostu podctawia przekonwertowany string



      Odp: Oczywiście, masz całkowitą rację. To nie jest żadne rzutowanie. Jakimś cudem rąbnąłem się na całej linii. Dziękuję za zwrócenie uwagi i przepraszam wszystkich czytelników PChar() jest oczywiście funkcją dokonujacą konwersji z typu String na PChar. Już poprawiłem.
    • Adam147852369
    • śro, 15 kwiecień 2009, 16:17
    • U mnie nic się nie dzieje! Napisz na gg: 6258673
Dexter