05 return false;
06 }
07 if (url.scheme() != "ftp") {
08 cerr << "Error: URL must start with 'ftp:'" << endl;
09 return false;
10 }
11 ftp.connectToHost(url.host(), url.port(21));
12 ftp.login();
13 QString path = url.path();
14 if (path.isEmpty())
15 path = "/";
16 pendingDirs.append(path);
17 processNextDirectory();
18 return true;
19 }
Выполнение функции getDirectory() начинается с некоторых основных проверок, и если все нормально, делается попытка установить FTP—соединение. Она отслеживает пути, которые необходимо будет обрабатывать, и вызывает функцию processNextDirectory(), чтобы начать скачивание корневого каталога.
01 void Spider::processNextDirectory()
02 {
03 if (!pendingDirs.isEmpty()) {
04 currentDir = pendingDirs.takeFirst();
05 currentLocalDir = "downloads/" + currentDir;
06 QDir(".").mkpath(currentLocalDir);
07 ftp.cd(currentDir);
08 ftp.list();
09 } else {
10 emit done();
11 }
12 }
Функция processNextDirectory() принимает первый удаленный каталог из списка каталогов, ожидающих обработки, pendingDirs, и создает соответствующий каталог в локальной файловой системе. После этого она указывает объекту QFtp на необходимость изменения каталога на принятый ею каталог и затем получения списка его файлов. Для каждого файла, обрабатываемого функцией list(), генерируется сигнал listInfo(), приводящий к вызову слота ftpListInfo().
Когда все каталоги оказываются обработанными, эта функция генерирует сигнал done(), обозначающий завершение скачивания.
01 void Spider::ftpListInfo(const QUrlInfo &urlInfo)
02 {
03 if (urlInfo.isFile()) {
04 if (urlInfo.isReadable()) {
05 QFile *file = new QFile(currentLocalDir + "/"
06 + urlInfo.name());
07 if (!file->open(QIODevice::WriteOnly)) {
08 cerr << "Warning: Cannot open file << qPrintable(
09 QDir::convertSeparators(file->fileName()))
10 << endl;
11 return;
12 }
13 ftp.get(urlInfo.name(), file);
14 openedFiles.append(file);
15 }
16 } else if (urlInfo.isDir() && !urlInfo.isSymLink()) {
17 pendingDirs.append(currentDir + "/" + urlInfo.name());
18 }
19 }
Параметр urlInfo слота ftpListInfo() содержит информацию о файле в сети. Если это обычный файл (не каталог) и его можно считывать, мы вызываем функцию get() для его загрузки. Объект QFile, используемый для загрузки файла, создается с помощью оператора new, и указатель на него хранится в списке openedFiles.
Если содержащиеся в QUrlInfo сведения об удаленном каталоге говорят, что он не является символической связью, этот каталог добавляется к списку pendingDirs. Мы пропускаем символические связи, поскольку они легко могут привести к бесконечной рекурсии.
01 void Spider::ftpDone(bool error)
02 {
03 if (error) {
04 cerr << "Error: " << qPrintable(ftp.errorString()) << endl;
05 } else {
06 cout << "Downloaded " << qPrintable(currentDir) << " to "
07 << qPrintable(QDir::convertSeparators(
08 QDir(currentLocalDir).canonicalPath()));
09 }
10 qDeleteAll(openedFiles);
11 openedFiles.clear();
12 processNextDirectory();
13 }
Слот ftpDone() вызывается после завершения всех команд FTP или при возникновении ошибки. Мы удаляем объекты QFile для предотвращения утечек памяти, а также для закрытия всех файлов. Наконец, мы вызываем функцию processNextDirectory(). Если какие-нибудь каталоги остались, весь процесс повторяется для следующего каталога в списке; в противном случае скачивание файлов прекращается и генерируется сигнал done().
Если ошибок нет, последовательность команд FTP и сигналов будет такой:
connectToHost(host, port)
login()
cd(directory_1)
list()
emit listInfo(file_1_1)
get(file_1_1)
emit listInfo(file_1_2)
get(file_1_2)
…
emit done()
…
cd(directory_N)
list()
emit listInfo(file_N_1)
get(file_N_1)
emit listInfo(file_N_2)
get(file_N_2)
…
emit done()
Если файл фактически оказывается каталогом, он добавляется в список pendingDirs и, когда завершается скачивание последнего файла, полученного текущей командой list(), выдается новая команда cd(), за которой следует новая команда list() для следующего каталога, ожидающего обработки, и весь процесс повторяется для нового каталога. Скачиваются новые файлы, и в список pendingDirs добавляются новые каталоги до тех пор, пока не будут скачаны все файлы из всех каталогов и список pendingDirs в результате не станет пустым.