Всю работу делает подкласс FtpGet, который определяется следующим образом:
01 class FtpGet : public QObject
02 {
03 Q_OBJECT
04 public:
05 FtpGet(QObject *parent = 0);
06 bool getFile(const QUrl &url);
07 signals:
08 void done();
09 private slots:
10 void ftpDone(bool error);
11 private:
12 QFtp ftp;
13 QFile file;
14 …
15 };
Класс имеет открытую функцию getFile(), которая считывает файл по указанному адресу URL. Класс QUrl имеет высокоуровневый интерфейс для извлечения различных частей URL, таких как имя файла, путь, протокол и порт.
Класс FtpGet имеет закрытый слот ftpDone(bool), который вызывается после окончания операции пересылки файла, и сигнал done(), который генерируется при завершении скачивания файла. Этот класс имеет также две закрытые переменные. Переменная ftp имеет тип QFtp и инкапсулирует соединение с сервером FTP; переменная file используется для записи скачанного из сети файла на диск.
01 FtpGet::FtpGet(QObject *parent)
02 : QObject(parent)
03 {
04 connect(&ftp, SIGNAL(done(bool)), this, SLOT(ftpDone(bool)));
05 }
В конструкторе мы подсоединяем сигнал QFtp::done(bool) к нашему закрытому слоту ftpDone(bool). QFtp генерирует сигнал done(bool) после завершения обработки всех запросов. Параметр типа bool показывает, возникла ошибка или нет.
01 bool FtpGet::getFile(const QUrl &url)
02 {
03 if (!url.isValid()) {
04 cerr << "Error: Invalid URL" << endl;
05 return false;
06 }
07 if (url.scheme() != "ftp") {
08 cerr << "Error: URL must start with 'ftp:'" << endl;
09 return false;
10 }
11 if (url.path().isEmpty()) {
12 cerr << "Error: URL has no path" << endl;
13 return false;
14 }
15 QString localFileName = QFileInfo(url.path()).fileName();
16 if (localFileName.isEmpty())
17 localFileName = "ftpget.out";
18 file.setFileName(localFileName);
19 if (!file.open(QIODevice::WriteOnly)) {
20 cerr << "Error: Cannot open "
21 << qPrintable(file.fileName()) << " for writing: "
22 << qPrintable(file.errorString()) << endl;
23 return false;
24 }
25 ftp.connectToHost(url.host(), url.port(21));
26 ftp.login();
27 ftp.get(url.path(), &file);
28 ftp.close();
29 return true;
30 }
Функция getFile() начинается с проверки переданного ей URL. Если возникла проблема, функция выводит в поток cerr сообщение об ошибке и возвращает false, указывая на неудачное скачивание файла.
Мы не обязываем пользователя указывать имя локального файла и пытаемся сами создать осмысленное имя на основе URL, а при неудаче используем имя ftpget.out. Если не удается открыть файл, мы печатаем сообщение об ошибке и возвращаем false.
Затем мы выполняем последовательность из четырех команд FTP, используя наш объект QFtp. Вызов url.port(21) возвращает номер порта, указанный в URL, или порт 21, если URL не содержит порта. Поскольку функции login() не передаются ни имя пользователи, ни пароль, делается попытка анонимного входа в систему. Второй аргумент функции get() задает выходное устройство ввода—вывода.
Команды FTP ставятся в очередь и обрабатываются в цикле обработки событий Qt. Завершение всех команд регистрируется сигналом done(bool) объекта QFtp, который мы подсоединили к слоту ftpDone(bool) в конструкторе.
01 void FtpGet::ftpDone(bool error)
02 {
03 if (error) {
04 cerr << "Error: " << qPrintable(ftp.errorString()) << endl;
05 } else {
06 cerr << "File downloaded as " << qPrintable(file.fileName()) << endl;
07 }
08 file.close();
09 emit done();
10 }
После выполнения всех команд FTP мы закрываем файл и генерируем сигнал done(). Может показаться странным, что мы закрываем файл именно здесь, а не после вызова ftp.close() в конце функции getFile(), но следует помнить, что команды FTP выполняются асинхронно и их выполнение вполне может быть еще не закончено после возврата управления функцией getFile(). Только после генерации объектом QFtp сигнала done() мы можем быть уверены, что скачивание файла завершено и теперь можно спокойно закрывать файл.