Главная arrow С++ (часть 2) arrow Подсистема загрузки файлов из Интернета J

Подсистема загрузки файлов из Интернета J

#include <iostream>
¦include <windows.h>
¦include <wininet.h>
¦include <fstream>
¦include <cstdio>
using namespace std;
const int MAX_EREMSG_SIZE = 80; const int MAX_FILENAME_SIZE = 512; Const int BUF_SIZE = 1024;
11 Класс исключений для ошибок загрузки, class DLExc {
Char err [ MAX_ERRMSG_S IZE] ; Public:
DLExc(char *exc)  {
if(strlen(exc) < MAX_ERRMSG_SIZE) strcpy(err, exc);
}
// Возвращает указатель на сообщение об ошибке, const char * geterr() return err;
}
};
// Класс для загрузки файлов из Интернета, class Download {
static bool ishttp(char *url);
static bool- httpverOK(HINTERNET hlurl);
static bool getfname(char *url, char *fname);
static unsigned long openfile(char *url, bool reload, ofstream &fout);
public:
static bool download(char *url, bool restart=false, void ("update)(unsigned long, unsigned long)=NULL);
};
// Загружает файл. //
// Передает URL файла параметру url. //
// Для дозагрузки файла передает true параметру reload. //
// Для определения функции update, которая вызывается после того,
// как очередной раз считан буфер, переда'ет указатель
//на эту.функцию как третий параметр. Если функция update
//не нужна, оставляет текущее значение
// третьего параметра, равное null.
bool Download::download(char *url, bool reload,
void (*update)(unsigned long, unsigned long))    {
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
try   {
if(lishttp(url))
throw DLExcC'Must be HTTP url.");
// Открывает файл, заданный в url.
// Открытый поток будет возвращаться
//в fout. Если reload равна true, данные
//в любом загружавшемся прежде файле будут удалены.
// Возвращается длина любого загружавшегося ранее файла
// (после возможного удаления данных).
filelen = openfile(url, reload, fout);
// Проверяет, доступно ли интернет-соединение, if(InternetAttemptConnect(O)   != ERROR_SUCCESS) throw DLExc("Can11 connect.");
// Открывает интернет-соединение. hlnet = InternetOpen("downloader",
INTERNET_OPEN_TYPE_DIRECT,
NULL, NULL,    0);
if(hlnet == NULL)
throw DLExc("Can't open connection.");
// Конструирует заголовок запрашиваемого интервала данных, sprintf(header,  "Range:bytes=%d-", filelen);
// Открывает URL и запрашивает диапазон, hlurl = InternetOpenUrl(hlnet, url, header,  -1,
INTERNET_FLAG_NO_CACHE_WRITE,   0); if(hlurl == NULL) throw DLExc("Can't open url.");
// Подтверждает, что поддерживается протокол HTTP/1.1 // или более поздние его версии, if(JhttpverOK(hlurl))
throw DLExc("HTTP/1.1 not supported.");
// Получает длину контента, len = sizeof contentlen; if(!HttpQuerylnfо(hlurl,
HTTP_QUERY_CONTENT_JJENGTH I
HTTP_QUERY_FLAG_NUMBER,
&contentlen, &len, NULL)) throw DLExc("File or content length not found.");
// Если существующий файл (при условии, что он есть) не завершен, // завершает его загрузку. if(filelen != contentlen && contentlen) do {
// Читает буфер of info.
if(!InternetReadFile(hlurl, &buf,
BUF_SIZE, &numrcved)) 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);
catch(DLExc)  {
}
fout.close();
InternetCloseHandle(hlurl); internetCloseHandle(hlnet);
throw; // повторно генерирует исключение для использования // вызывающей программой (caller)
}
fout.close();
InternetCloseHandle(hlurl); InternetCloseHandle(hlnet);
return true;
}
// Возвращает true, если у протокола HTTP версия 1.1 или более поздняя, bool Download::httpverOK(HINTERNET hlurl)   {
char str[80];
unsigned long len = 79;
// Получает версию протокола HTTP.
if (IHttpQueryInfo(hlurl, HTTP_QUERY_VERSION, &str, &len, NULL)) return false;
// Сначала проверяет главный номер версии HTTP.
char *р = strchr(str,  '/*);
Р++;
if(*p == '0') return false; // can use HTTP 0.x
/ / Теперь находит начало младшего номера версии HTTP.
Р = strchr(str,   '.');
Р++;
// Преобразует в тип int. int minorVerNum = atoi (p) ;
if (minorVerNum > 0) return true; return false;
// Выделяет имя файла из URL. Возвращает false, если
// имя файла не может быть найдено.
bool Download::getfname(char *url, char *fname)  {
// Находит последний слэш (/).
char *p = strrchr(url,  '/');
// Копирует имя файла после последнего знака /. if(р && (strlen(p) < MAX_FILENAME_SIZE))   { р++;
strcpy(fname, р); return true,-
}
else
return false;
}
// Открывает файл вывода, инициализирует поток
// вывода и возвращает длину файла. Если
// reload равна true, сначала укорачивает до нуля любой
// уже существующий файл.
unsigned long Download::openfile(char *url,
bool reload,
ofstream &fout)  {
char fname[MAX_FILENAME_SIZE]; i f(!getfname (url, fname))
throw DLExc("File name error.");
if(!reload)
fout.open(fname, ios::binary | ios::out | ios::app | ios::ate);
else
fout.open(fname, ios:: binary | ios::out | ios::trunc);
r
if(!fout)
throw DLExc("Can't open output file."); // Получает текущую длину файла.
return fout.tellp();
)
// Подтверждает, что в URL задан HTTP, tool Download::ishttp(char *url)  { char str[5] = "";
// Получает первые 4 символа из URL. strncpy(str, url, 4);
// Преобразует их в нижний регистр. for(char *p=str; *р,- р++) *р = tolower(*p);
return !strcmp("http", str);
}
В листинге 5.1 определены два класса. Первый, DLEXC, — класс, в котором инкапсулированы все исключения, генерируемые загрузчиком. Конструктор класса DLEXC получает указатель на строку, описывающую исключение, и сохраняет ее. Указатель на сообщение об ошибке получается с помощью вызова функции-члена класса geterr ().
Второй класс — Download — обеспечивает загрузку файлов из Интернета. Обратите внимание на то, что этот класс состоит только из статических (static) функций. Следовательно, создание этого класса — скорее организационный прием, чем средство для инкапсуляции. Действительно, в данном случае можно было обойтись пространством имен вместо описания класса. Однако применение класса позволяет объявить некоторые функции со спецификатором доступа private, тем самым препятствуя их использованию в другом коде, и облегчает включение новых функциональных возможностей в дальнейшем.
В следующих разделах подробно обсуждается класс Download.