Изготовление, наружная реклама. |
|
|
| |
Главная С++ (часть 2) Функция downloadQ
Это единственная функция класса Download, описанная со спецификатором доступа public. Она вызывается в коде пользователя и обеспечивает загрузку файла из Интернета. Следовательно, для загрузки файла вы вызываете downioado. Поскольку она очень важна, рассмотрим каждую строку кода функции. Она начинается со строк: bool Download::download(char *url, bool reload, void (*update)(unsigned long, unsigned long)) { у функции downioado три параметра. Первый — url — указатель на строку, держащую полный URL-адрес файла (включая его имя). Предполагается, что ИМЯ файла располагается в этой строке после последнего символа "/" (слэш). Например, в приведенной далее строке URL-адреса имя файла — MyFiIe.dat: http://www.SoeSite.com/MyFile.dat Помните, что надо задавать полный URL-адрес, используя его каноническую форму, включающую http://. Второй параметр, reload, определяет, будет ли файл загружаться повторно. Если переменная reload имеет значение true, файл будет загружаться полностью, независимо от того, есть ли он на диске (целиком или только часть). Если же значение переменной false, то загружается только остаток файла (если он есть). Таким образом, если вы хотите получить свежую копию файла, который вы уже загрузили из Интернета, передайте значение true В параметре reload. Третий параметр функции — update. Это указатель на функцию, которая будет периодически вызываться из функции downioado для уведомления кода пользователя о состоянии процесса загрузки. Если значение этого параметра — null, функция update не применяется. Мы вернемся к обсуждению требований к этому параметру немного позднее. Далее объявляются следующие локальные переменные: ofstream fout; // выходной поток unsigned char buf[BUF_SIZE]; // входной буфер unsigned long numrcved; // количество считанных байтов unsigned long filelen; // длина файла на диске HINTERNET hlurl, hlnet; // интернет-дескрипторы unsigned long contentlen;// длина контента unsigned long len; // длина contentlen unsigned long total =0; // накапливаемый итог полученных байтов char header[80]; // содержит Range header Обратите внимание на переменные hiurl и ninet. Они содержат дескрипторы для загружаемого ресурса URL и интернет-соединения соответственно. Тот или иной из этих дескрипторов используется API-функциями библиотеки WinlNet. Код функции main начинается с приведенных далее строк. Обратите внимание на то, что весь код загрузчика заключен в блок try/catch, который обрабатывает соединение и/или файловые ошибки, try { if(!ishttp(url)) throw DLExc("Must be HTTP url.-); Прежде всего, функция downioado вызывает функцию ishttp о для того чтобы выяснить, задан ли в URL-адресе HTTP-запрос, т. е. URL-адрес начинается с комбинации символов "http". Как объяснялось ранее, только HTTP-запросы обрабатываются загрузчиком файлов из Интернета. Далее полученный файл открывается и определяется его длина. // Открывает файл, заданный в url. // Открытый поток будет возвращаться //в fout. Если reload равна true, данные // любого загружавшегося прежде файла будут удалены. // Возвращается длина любого загружавшегося ранее файла // (после возможного удаления данных). filelen = openfile(url, reload, fout); Как объясняется в комментариях, если на диске нет файла или надо его загрузить заново, длина любого загруженного ранее файла с заданным именем будет укорочена до нуля и длина файла, возвращаемая функцией openfiieo, будет равна нулю. В противном случае, если файл на диске есть, будет возвращена его длина. Помните, если загрузка была прервана, к моменту ее рестарта на диске может существовать частичный файл. После открытия файла устанавливается соединение с Интернетом с помощью приведенного далее кода: // Проверяет, доступно ли интернет-соединение, if(InternetAttemptConnect(O) != ERROR_SUCCESS) throw DLExc("Can't connect.") ; // Открывает интернет-соединение, hlnet = InternetOpenC'downloader", INTERNET_OPEN_TYPE_DIRECT,' NULL, NULL, 0); if(hlnet == NULL) throw DLExc("Can"t open connection."); Сначала делается попытка установить соединение с Интернетом с помощью вызова API-функции mternetAttemptconnect о из библиотеки WinlNet. Функция возвращает ERROR_SUCCESS, если оно возможно. Единственный аргумент функции зарезервирован и должен быть нулевым. Если в этот момент компьютер не соединен с Интернетом, вы увидите диалоговое окно (в соответствии с установками на вашем компьютере), спрашивающее о том, не хотите ли вы установить соединение. Если подключение к Интернету Невозможно, фуНКЦИЯ InternetAttemptConnectО вернет ошибку, ВЫЗЫ" вающую генерацию исключения. Предположим, что соединение доступно, тогда для открытия соединения вызывается функция mtemetopen (). Это еще одна API-функция из библиотеки WinlNet, далее приведен ее прототип. gjNTERNET InternetOpen(LPCTSTR agent, DWORD access, LPCTSTR proxy, LPCTSTR proxopt, DWORD options); Параметр agent задает имя приложения, access — тип доступа. Для нашего загрузчика значение типа доступа равно INTERNET_OPEN_TYPE_DIRECT. В этом случае параметры proxy и proxopt не используются и должны быть равны null- В параметре options устанавливаются дополнительные характеристики соединения, не требующиеся загрузчику. Функция возвращает дескриптор открытого соединения. Если соединение не может быть открыто, возвращается null. Как только соединение открывается, генерируется заголовок Range. // Конструирует заголовок запрашиваемого интервала данных, sprintf(header, "Range:bytes=%d-", filelen); Далее заголовок передается функции mternetopenuri () из библиотеки WinlNet, которая открывает URL-адрес, заданный в параметре url. // Открывает URL-адрес' и запрашивает диапазон, hlurl = InternetOpenUrl(hlnet, url, header, -1, INTERNET_FLAG_NO_CACHE_WRITE, 0); if (hlurl == NULL) throw DLExc("Can't open url."); Прототип этой функции приведен далее. HINTERNET InternetOpenUrl(HINTERNET hJnet, LPCTSTR url, LPCTSTR headerstr, DWORD headlen, DWORD options, LPDWORD extra); В параметре hJnet передается дескриптор Интернет-соединения, получений из функции mtemetopeno. В параметре url приводится указатель на строку, содержащую URL-адрес, который надо открыть. Указатель на строку с одним или несколькими дополнительными заголовками, которые нужно включить в запрос, передается в параметре headers tr. Длина его содержит-Ся в параметре headlen, который может быть равен -1, если параметр Naders tr указывает на нуль-терминированную строку, как это сделано в ^грузчике. Различные флаги могут быть заданы в параметре options. Далее пРИведен один из них, использующийся в загрузчике: I№TERNET_FLAG_NO_CACHE_WRITE Он препятствует кэшированию файла, загружаемого из Интернета. Любую дополнительную информацию, свойственную приложению, можно передать в параметре extra. Поскольку в загрузчике в ней нет необходимости, значение параметра равно нулю. Функция возвращает дескриптор открытого URL-pecypca или null в случае ошибки. Каждый ответ сервера передается с заголовком. Один из его компонентов содержит версию протокола HTTP. Поскольку загрузчик файла из Интернета требует, чтобы сервер поддерживал протокол HTTP версии 1.1 или более поздние, перед загрузкой файла необходима проверка версии протокола. Она выполняется с помощью вызова функции httpverOKO, как показано далее. // Подтверждает, что поддерживается протокол HTTP/1.1 // или более высокие его версии, if(!httpverOK(hlurl)) throw DLExc("HTTP/1.1 not supported."); Предположим, что версия протокола приемлема, тогда на следующем этапе определяется объем загружаемого контента с помощью вызова функции HttpQueryinf о (), приведенного далее. // Получает длину контента, len = sizeof contentlen; if(!HttpQueryinfо(hlurl, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &contentlen, &len, NULL)) throw DLExc("File or content length not found."); После возврата функции HttpQueryinf о () длина контента (в байтах) хранится в переменной contentlen. Она автоматически учитывает интервал, заданный в заголовке Range. Следовательно, если файл загружается целиком, требуемый интервал данных равен "0-" (он соответствует целому файлу) и длина контента равна длине файла. Когда возобновляется прерванная ранее загрузка, величина интервала будет определять начальную точку где-то внутри файла и длина контента будет равна числу оставшихся байтов. Функция HttpQueryinfo() получает информацию из заголовка. Далее приведен ее прототип. BOOL HttpQueryinfо (HINTERNET hJurl, DWORD what, LPVOID buf, LPDWORD buflen, LPDWORD index); Дескриптор запроса, в данном случае возвращенный функцие" InternetOpenUrl о, передается в параметре hJuri. Именованная константазадающая характер получаемой информации, указывается в параметре what. Для загрузчика требуются две константы. Первая j0<TP_QUERY_CONTENT_LENGTH задает запрос длины контента (в данном случае файла), и вторая lfTTP_QUERY_FLAG_NUMBER требует, чтобы эта величина была представлена как целое значение. Указатель на буфер, получающий информацию, передается в параметре buf, а указатель на целое, определяющее длину буфера, содержится в параметре buf len. В параметре index можно задать индекс заголовка, но загрузчик не использует этот параметр, поэтому в нем содержится значение null. Функция возвращает true, если она может получить необходимую информацию. Если длина контента недоступна, функция HttpQueryinfoO возвращает false. Допустим, что длина контента доступна, если длина имеющегося на диске файла меньше ее и длина контента не равна нулю, файл (или его оставшаяся часть) загружается из Интернета с помощью приведенных далее строк кода. // Если существующий файл (при условии, что он есть) не завершен, // завершает его загрузку, if (filelen != contentlen && contentlen) do { // Читает буфер of info. if(!InternetReadFile(hlurl, &buf, BUF_SIZE, bnumrcved)) throw DLExc("Error occurred during download."); // Записывает буфер на диск. fout.write((const char *) buf, numrcved); if(!fout.good()) throw DLExc("Error writing file."); total += numrcved; // обновляет накапливаемый итог // Вызывает функцию update, если она задана. if(update && numrcved > 0) update(contentlen+filelen, total+filelen); > while(numrcved > 0) ; else if (update) update(filelen, filelen); В ЗагруЗЧИКе ДЛЯ ЧТеНИЯ файла применяется фуНКЦИЯ InternetReadFileo из библиотеки WinlNet, из буфера. В каждом проходе цикла вызывается функция, на которую ссылается переменная update, если ее значение не null. Как уже объяснялось ранее, функция, на которую указывает update применяется для формирования отчета о состоянии процесса загрузки файла. Функция InternetReadFileo очень полезна, так как позволяет вам читать файл из Интернета почти так же, как вы считываете файл с диска. Далее приведен ее прототип. BOOL InternetReadFile(HINTERNET hlurl, LPVOID buf, DWORD numbytes, LPDWORD numrcvd) ; Дескриптор файла передается в параметре hiuri. В загрузчике это дескриптор, возвращенный функцией mtemetoperuri (). Указатель на буфер, который будет получать данные, содержится в параметре buf, а количество считываемых байтов — в параметре numbytes. Это число не должно превышать размер buf. Количество действительно считанных байтов возвращается в переменной, на которую указывает параметр numrcvd. Значение этой переменной станет равно нулю, когда не останется байтов для считывания. В случае успешного завершения функция возвращает true, в противном случае — false. Завершается функция downioado следующими строками. } catch(DLExc) fout.close(); InternetCloseHandle(hlurl); InternetCloseHandle(hlnet); throw; // повторно генерирует исключение для использования вызывающей // программой (caller) } fout.close(); InternetCloseHandle(hlurl); InternetCloseHandle(hlnet); return true; } В случае удачного завершения функция downioado закрывает файл и интернет-дескрипторы и возвращает true. Если возникает ошибка, дескрипторы также закрываются, и затем повторно генерируется исключение, что позволяет в пользовательском коде предусмотреть реакцию на ошибку.
|
|
|
|
|