× Aktuálně z oboru

Programátoři po celém světě dnes slaví Den programátorů [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]
Celá zprávička [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]

Sťahovanie súborov z internetu pomocou C/C++

[ http://programujte.com/profil/13267-matej-tomcik/ ]Google [ ?rel=author ]       [ http://programujte.com/profil/118-zdenek-lehocky/ ]Google [ ?rel=author ]       7. 3. 2010       30 701×

Jednoduché riešenie pre vaše programy, ktoré potrebujú sťahovať súbory z Internetu. Ak ste uvažovali o rozšírení svojej aplikácie alebo minimálne vašich vedomostí o možnosť sťahovať dáta z Internetu pomocou C++, tak po prečítaní tohto článku by vám to nemalo robiť problém.

K dosiahnutiu tohto cieľa existuje viacero ciest, no pre programátorov v prostredí Windows je najefektívnejšie využiť WinInet. Dôvody, prečo je tomu tak, v krátkosti objasním.

K súborom, ktoré sa nachádzajú v sieti Internet, sa najčastejšie pristupuje pomocou protokolu HTTP. HTTP teda slúži na výmenu dát medzi klientom a serverom. Nachádza sa na 4.  (aplikačnej) vrstve TCP/IP modelu. Pod ním sa nachádza vrstva transportná, sieťová a fyzická. HTTP je závislé na TCP (Transmission Control Protocol), ktoré je svojim protokolom náročné na aplikovanie. WinInet sa však stará o tieto problémy za nás, a nám sa tak ponúka komfortné implementovanie HTTP služieb.

Samotný postup sťahovania dát z Internetu pomocou WinInet sa skladá z niekoľkých krokov. Prvým z nich je nadviazanie spojenia, resp. vytvorenie inštancie, ktorá sa použije pri otváraní internetových súborov, alebo ešte inak povedané, inicializovanie WinInet pre použitie v aplikácii. Druhým krokom je otvorenie internetového súboru a tretím samotné prečítanie tohto súboru. Tieto tri základné kroky sú však v tomto článku rozšírené, aby sme si ukázali postupy, ktoré je vhodné dodržať. Napríklad zistenie statusu práve otvoreného internetového súboru nie je nutné, no môže nám povedať, či sa daný súbor otvoril, zistiť jeho veľkosť a iné, i keď menej dôležité informácie.

Začal by som popisom jednotlivých funkcií, ktoré v tomto programe použijeme. WinInet obsahuje množstvo užitočných funkcií, no rád by som vám poskytol len základy, na ktorých sa môžete realizovať a rozvíjať. Pre viac informácií môžete navštíviť referencie na MSDN [ http://msdn.microsoft.com/en-us/library/aa385473(VS.85,lightweight).aspx ].

  • Prvou takou funkciou, ktorú v našom programe použijeme je:
    DWORD InternetAttemptConnect(DWORD rezervovane);
    Ako už z názvu vyplýva, táto funkcia sa pokúsi o spojenie s Internetom. Lepšie túto funkciu popísať neviem, no na MSDN je trochu presnejší popis, i keď jej úloha nie je až také podstatné, program by fungoval aj bez nej, ale vždy je lepšia prevencia než liečba . Funkcia vracia ERROR_SUCCESS pri úspešnom spojení.
  • HINTERNET InternetOpen(LPCTSTR agent,DWORD typPristupu,LPCTSTR nazovProxy,LPCTSTR proxyBypass,DWORD priznaky);
    Touto funkciou inicializujeme našu aplikáciu na používanie WinInet funkcií. Pri úspešnom inicializovaní obdržíme handler, ktorý sa bude ďalej používať napr. otváraní internetových súborov. agent predstavuje názov vášho agenta, typPristupu je spôsob, akým budeme pristupovať na Internet, resp. či sa bude používať proxy atď. S týmto súvisia aj ďalšie parametre, a to proxyBypass a priznaky.
  • HINTERNET InternetOpenUrl(HINTERNET hInternet,LPCTSTR url,LPCTSTR hlavicky,DWORD dlzkaHlaviciek,DWORD priznaky,DWORD_PTR kontext);
    Otvoríme požadovaný súbor pomocou URL, či už HTTP alebo FTP. My však v tomto programe budeme používať HTTP. Ako hInternet použijeme hodnotu vrátenú funkciou InternetOpen. Parametrom hlavicky špecifikujeme vlastné hlavičky, ktoré sa odošlú spolu s požiadavkou. Funkcia nám vracia handle na otvorený internetový súbor (skôr sa stretnete s pojmom resource).
  • BOOL InternetReadFile(HINTERNET internetovySubor,LPVOID zasobnik,DWORD pocetBajtovNaPrecitanie,LPDWORD pocetPrecitanychBajtov);
    Touto funkciou prečítame určitú časť nášho internetového súboru, resp. v tomto prípade už odpoveď servera. internetovySubor nám určuje, ktorú resource ideme čítať. Tento parameter sme dostali funkciou InternetOpenUrl. Ostatné parametre sú známe. Výsledok funkcie je buď true alebo false. Výsledok true dostaneme aj vtedy, keď sme súbor už prečítali, čiže musíme kontrolovať počet bajtov, ktoré táto funkcia z resource prečítala, a ak sa táto hodnota rovná nule, ukončiť čítanie.
  • BOOL HttpQueryInfo(HINTERNET request,DWORD typInformacie,LPVOID zasobnik,LPDWORD velkostZasobnika,LPDWORD index);
    Túto funkciu môžeme použiť, ak chceme z odpovede servera získať informáciu napr. o statuse odpovede, veľkosti obsahu a pod. Za request v našom prípade dosadíme handle vrátený funkciou InternetOpenUrl. typInformacie nám určuje, ktorú informáciu chceme z hlavičky prečítať. Kompletný zoznam je na MSDN [ http://msdn.microsoft.com/en-us/library/aa385351(VS.85).aspx ]. Do premennej na adrese zasobnik sa uloží výsledok.
//---------------------------------------------------------------------------------
// Sťahovanie súborov z internetu pomocou WinInet
//---------------------------------------------------------------------------------
#include 
#include 
#include 
#include 
#include    // hlavičkový súbor knižnice WinInet
//---------------------------------------------------------------------------------
#define VELKOST_VYROVNAVACEJ_PAMATE   256   // veľkosť vyrovnávacej pamäte použitej
                              // pri čítaní internetového súboru
//---------------------------------------------------------------------------------
#pragma comment(lib,"WinInet.lib")   // prilinkovanie knižnice WinInet
//---------------------------------------------------------------------------------
using namespace std;
//---------------------------------------------------------------------------------

int main(int argc, char * argv[])
{
   cout << "Stahovanie suborov z internetu" << endl;
   string url,subor;
   cout << "Zadajte URL suboru: ";
   getline(cin,url);
   cout << "Zadajte nazov suboru na ulozenie: ";
   getline(cin,subor);

   // V prvom rade si deklarujeme premenné, ktoré budeme používať
   HINTERNET spojenie,internetovySubor;
   // Ďalej sa pokúsime o spojenie
   if (InternetAttemptConnect(0) != ERROR_SUCCESS)
   {
      // Spojenie nie je možné nadviazať, resp. nie je možné pracovať v online móde
      cout << "Nebolo mozne sa pokusit o spojenie" << endl;
      cout << "Kod chyby: " << GetLastError() << endl;
      return 1;
   }
   // Teraz si vytvoríme spojenie, inicializujeme aplikáciu na používanie WinInet
   spojenie = InternetOpenA("Moj internetovy prehladavac",
                     INTERNET_OPEN_TYPE_PRECONFIG,   // Predvolené nastavenia
                     0,                        // Žiadne proxy
                     0,                        
                     0);                        // Žiadne príznaky
   if (spojenie == 0) // NULL
   {
      cout << "Nie je mozne inicializovaz WinInet" << endl;
      cout << "Kod chyby: " << GetLastError() << endl;
      return 1;
   }
   // Následne otvoríme spojenie so súborom, ktorý je definovaný pomocou URL
   internetovySubor = InternetOpenUrlA(spojenie,      // Inštancia spojenia
                              url.c_str(),   // URL adresa súboru
                              0,            // Žiadna prídavná HTTP hlavička
                              0,            // Dĺžka prídavnej hlavičky   
                              0,            // Žiadne príznaky
                              0);
   if (internetovySubor == 0) // NULL
   {
      cout << "Nie je mozne vytvorit spojenie so suborom" << endl;
      DWORD kodChyby = GetLastError();
      cout << "Kod chyby: " << kodChyby << endl;
      char chyba[256];
      DWORD chybaVelkost = 255;
      if (InternetGetLastResponseInfoA(&kodChyby,chyba,&chybaVelkost))
         cout << "Popis chyby: " << chyba << endl;
      // Nesmieme zabudnúť zatvoriť už inicializovaný handler s WinInet
      InternetCloseHandle(spojenie);
      return 1;
   }
   // V tomto kroku môžeme zistiť podrobnosti o súbore
   DWORD informacia;
   DWORD velkostInformacie = sizeof(DWORD);
   // Najskôr si zistíme HTTP status kód (napr. status kód 200 znamená OK, 404 znamená súbor nenájdený)
   if (!HttpQueryInfoA(internetovySubor,   // Inštancia nami vytvoreného spojenia so súborom
               HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,   // Požadujeme status ktorý je číselným typom
               &informacia,         // Sem bude zapísaný výsledok volania
               &velkostInformacie,      // Veľkosť dátovej jednotky informácie
               0))
   {
      cout << "Nie je mozne zistit kod statusu" << endl;
      cout << "Kod chyby: " << GetLastError() << endl;
      InternetCloseHandle(internetovySubor);
      InternetCloseHandle(spojenie);
      return 1;
   }
   cout << "Status odpovede: " << informacia << endl;
   // Ak sa status odpovede nerovná 200, nedostaneme súbor, ktorý sme požadovali
   if (informacia != HTTP_STATUS_OK)
   {
      cout << "Status spojenia NIE je OK" << endl;
      InternetCloseHandle(internetovySubor);
      InternetCloseHandle(spojenie);
      return 1;
   }
   // Teraz si zistíme veľkosť súboru
   if (!HttpQueryInfoA(internetovySubor,   // Inštancia nami vytvoreného spojenia so súborom
               HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,   // Požadujeme status ktorý je číselným typom
               &informacia,         // Sem bude zapísaný výsledok volania
               &velkostInformacie,      // Veľkosť dátovej jednotky informácie
               0))
   {
      // Síce sme nezistili veľkosť súboru, ale to nám neprekáža v jeho stiahnutí
      cout << "Velkost suboru sa neda zistit" << endl;
      informacia = 0;
   }
   // Vytvoríme si vyrovnávaciu pamäť, do ktorej budeme ukladať časti súboru
   char * vyrovnavaciaPamat = new char[VELKOST_VYROVNAVACEJ_PAMATE];
   // Vytvoríme si výstupný súbor
   fstream vystupnySubor(subor.c_str(),   // Názov výstupného súboru
                  ios_base::out | ios_base::binary);   // Budeme zapisovať v binárnom móde
   // Premenná, do ktorej sa bude ukladať koľko sme z internetového súboru prečítali
   DWORD precitane = 0;
   // Premenná, do ktorej uložíme celkový počet stiahnutých bajtov
   DWORD stiahnute = 0;

   cout << "Stahujem\t\t\t";
   while (InternetReadFile(internetovySubor,   // Spojenie na náš internetový súbor
                     vyrovnavaciaPamat,   // Vyrovnávacia pamäť
                     VELKOST_VYROVNAVACEJ_PAMATE,   // Koľko môžeme max. prečítať
                     &precitane))      // Koľko WinInet prečítal
   {
      if (precitane == 0)      // Už sme všetko prečítali, ukončiť
         break;
      stiahnute += precitane;
      if (informacia != 0)   // Ak vieme o veľkosti súboru môžeme vypočítať %
         cout << "\rStahujem " << (int)((stiahnute*100)/informacia) << "%";
      else               // Inak len vypíšeme celkový počet stiahnutých B
         cout << "\rStahujem " << stiahnute << " B";
      // Zapísať do vsýtupného súboru
      vystupnySubor.write((const char*)vyrovnavaciaPamat,precitane);
   }
   cout << endl << endl;
   // Zistíme chybu (ak nejaká nastala)
   DWORD chyba = GetLastError();
   if (chyba != 0)
   {
      cout << "Pri citani vznikla chyba" << endl;
      cout << "Kod chyby: " << chyba << endl;
      char chybaText[256];
      DWORD chybaVelkost = 255;
      if (InternetGetLastResponseInfoA(&chyba,chybaText,&chybaVelkost))
         cout << "Popis chyby: " << chyba << endl;
   }
   else
   {
      cout << "Hotovo" << endl;
   }
   // Zatvoríme spojenia
   InternetCloseHandle(internetovySubor);
   InternetCloseHandle(spojenie);
   // Zatvoríme výstupný súbor
   vystupnySubor.close();
   // Nezabudneme vyrovnávaciu pamäť uvoľniť :)
   delete [] vyrovnavaciaPamat;
   return 0;
}

Už funkčný exe súbor spolu s kódom nájdete na: http://router.xf.cz/cppfiledownload.zip [ http://router.xf.cz/cppfiledownload.zip ]


Článek stažen z webu Programujte.com [ http://programujte.com/clanek/2010012200-stahovanie-suborov-z-internetu-pomocou-c-c/ ].