Разработка приложений для Internet

       

Метод FtpFileDownload класса CFtpViewDlg


Метод FtpFileDownload класса CFtpViewDlg имеет единственный параметр sFileName, через который ему передается строка, содержащая имя файла на сервере FTP. Этот файл надо загрузить на локальный компьютер:

BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )

{

}

Первая часть метода FtpFileDownload практически полностью соответствует более ранним версиям этого метода. В ней создается и отображается на экране стандартная диалоговая панель Save As. Эта панель позволяет пользователю указать имя файла и каталог на локальном компьютере в который записывается файл, загружаемый с сервера FTP. По умолчанию в качестве имени файла предлагается использовать имя загружаемого файла. Это имя передается конструктору класса CFileDialog через параметр sFileName:

// Создаем объект, управляющий панелью Save As

CFileDialog mFileOpen(FALSE, NULL, sFileName);

// Отображаем панель Save As на экране

int result = mFileOpen.DoModal();

После того как пользователь выберет файл и нажмет кнопку OK, или откажется от выбора и нажмет кнопку Cancel, диалоговая панель Save As закрывается и метод DoModal возвращает соответствующий код завершения.

Если пользователь отказался от выбора файла и нажал кнопку Cancel, то метод DoModal возвращает значение IDCANCEL. В этом случае мы только выводим соответствующее предупреждающее сообщение и завершаем работу метода FtpFileDownload не выполняя загрузки файла:

if(result == IDCANCEL)

{



   AfxMessageBox("File not selected");

   return FALSE;

}

Если пользователь выбрал файл и нажал кнопку OK (или выбрал файл, выполнив по его имени двойной щелчок левой кнопкой мыши), то метод DoModal возвращает значение IDOK. В этом случае мы определяем полный путь выбранного пользователем файла и записываем его в строку sLocalFileName:

CString sLocalFileName;

. . .

else if(result == IDOK)

{

   sLocalFileName = mFileOpen.GetPathName();

}

Через единственный параметр метода FtpFileDownload ему передается строка sFileName, содержащая имя файла выбранного пользователем для загрузки с сервера. Чтобы получить полный путь этого файла на сервере мы добавляем имя файла к текущему каталогу. Результат записываем обратно в строку sFileName:


// Формируем полное имя файла для загрузки его с сервера FTP

sFileName = sCurentDirectory + "/" + sFileName;

Чтобы мы смогли определить во время загрузки файла какая его часть загружена, необходимо узнать размер файла. Для этой цели мы создаем объект класса CFtpFileFind и выполняем поиск данного файла на сервере.

Сначала мы вызываем метод FindFile, передавая ему в качестве параметра полный путь файла, который надо найти на сервере, а затем, в случае успеха, вызываем метод FindNextFile который позволит нам определить характеристики, и в том числе длину, обнаруженного файла.

Надо заметить, что файл sFileName обязательно будет обнаружен, так как мы уже нашли его раньше (см. метод DirectoryView). Конечно мы не учитываем в этом случае возможность разрыва связи, удаление файла администратором и сервера ит. д.:

// Создаем объект класса CFtpFileFind

CFtpFileFind m_FtpFileFind(m_FtpConnection);

// Выполняем поиск выбранного нами файла

if(fResult =

   m_FtpFileFind.FindFile(_T(sFileName)))

{

   // Если поиск закончился успешно, получаем его

   // характеристики

   fResult = m_FtpFileFind.FindNextFile();

   . . .

}

Повторный поиск файла мы выполняем исключительно потому, что так наиболее просто узнать его размер. Для этого достаточно вызвать метод GetLength:

// Временная переменная для хранения размера файла

DWORD dwLength = 0;

// Определяем длину файла

dwLength = m_FtpFileFind.GetLength();

В соответствии с длинной загружаемого файла устанавливаются новые границы изменения значений для линейного индикатора и новый шаг приращения.

Мы будем загружать файл с сервера FTP блоками по READ_BLOCK байт в каждом (последний блок может иметь меньшую длину). Поэтому мы сможем изменить состояние линейного индикатора dwLength / READ_BLOCK раз:

// Устанавливаем новые границы для линейного индикатора

m_LoadProgress.SetRange(0, (int)(dwLength/READ_BLOCK) );

// Устанавливаем шаг приращения равный единице

m_LoadProgress.SetStep(1);



Заметим, что чем меньше загружаемый файл, тем более резко будет меняться значение линейного индикатора, отражающего процесс загрузки. Если размер загружаемого файла будет меньше, чем размер буфера (READ_BLOCK), то линейный индикатор вообще не будет задействован. В принципе, вы можете сделать линейный индикатор более чувствительным, если будете загружать файл меньшими порциями - уменьшите значение READ_BLOCK.

Перед тем как приступить к самому интересному - загрузке файла с сервера FTP, заканчиваем поиск и вызываем метод Close  для объекта m_FtpFileFind:

// Так как мы искали только один файл, заканчиваем поиск

m_FtpFileFind.Close();

Перед тем как приступить к загрузке файла с сервера мы должны его открыть. Для этого следует воспользоваться методом OpenFile, входящим в состав класса CFtpConnection. Он возвращает указатель на объект класса CInternetFile:

// Определяем указатель на файл Internet 

CInternetFile* iFile;

// Открываем выбранный нами файл на сервере FTP

iFile = m_FtpConnection -> OpenFile(

   sFileName.GetBuffer(MIN_LEN_BUF),

   GENERIC_READ,

   FTP_TRANSFER_TYPE_BINARY

);

Первый параметр метода OpenFile задает имя файла, который надо открыть. Второй параметр выбирает режим в котором открывается файл. Константа GENERIC_READ означает, что файл открывается для чтения. Третий и последний параметр метода OpenFile устанавливает режим передачи двоичных данных. Мы выбрали этот режим чтобы иметь возможность загружать не только текстовые, но и двоичные файлы.

Файл, получаемый с сервера FTP, мы должны сохранить на локальном компьютере в файле под именем sLocalFileName. Создаем файл sLocalFileName, используя возможности класса CFile:

// Создаем и открываем файл на локальном диске компьютера

CFile fLocalFile(

         sLocalFileName,

         CFile::modeCreate | CFile::modeWrite

      );

Файл создается с атрибутами CFile::modeCreate и CFile::modeWrite, поэтому если файла с именем sLocalFileName нет - он будет создан, а если такой файл уже есть, то он перезаписывается. У пользователя при этом запрашивается подтверждение на перезапись файла.



Для чтения информации из файла на сервере мы будем обращаться к методу Read класса CInternetFile. Этот метод считывает порцию данных из файла и записывает их во временный буфер. Буфер мы предварительно создаем, при помощи функции malloc:

// Создаем буфер для чтения файла с сервера FTP

void* ptrBuffer;

ptrBuffer = malloc( READ_BLOCK );

Затем в цикле мы приступаем к чтению данных из файла на сервере и записи их в файл на локальном компьютере. Цикл будет продолжаться до тех пор, пока не будет достигнут конец файла.

Операция чтения файла с сервера выполняется методом Read, определенным в классе CInternetFile. Каждый вызов метода Read считывает READ_BLOCK байт и записывает их в буфер ptrBuffer. Количество байт, которое действительно было считано из файла на сервере, возвращается методом Read и записывается во временную переменную nReadCount.

Как только после вызова метода Read значение nReadCount станет меньше READ_BLOCK значит достигнут конец файла и можно выходить из цикла чтения:

UINT nReadCount = 0;

do

{

   . . .

   // Читаем из файла на сервере READ_BLOCK байт

   nReadCount = iFile -> Read( ptrBuffer, READ_BLOCK );

   . . .

} while (nReadCount == READ_BLOCK);

Если во время чтения файла с сервера возникнет ошибка, например разорвется связь, то метод Read вызовет соответствующее исключение CInternetException. Мы организуем обработку такой ситуации и помещаем вызов метода Read в блок try, а ниже определяем блок catch:

try

{

   // Читаем из файла на сервере READ_BLOCK байт

   nReadCount = iFile -> Read( ptrBuffer, READ_BLOCK );

}

Блок catch выполняет обработку исключения CInternetException. Обращаясь к методу GetErrorMessage мы получаем текстовое описание причины, вызвавшей исключение и отображаем ее на экране. Если метод GetErrorMessage не может определить причину исключения, он возвращает нулевое значение и мы отображаем на экране строку GetFtpConnection Error:

catch (CInternetException* pEx)

{

   // Обрабатываем исключение CInternetException



   TCHAR szErr[1024];  // временный буфер для сообщения

   // Выводим сообщение об ошибке

   if (pEx->GetErrorMessage(szErr, 1024))

      AfxMessageBox(szErr);

   else

      AfxMessageBox("GetFtpConnection Error");

   // Так как возникла ошибка, значит данные не считаны

   nReadCount = 0;

   // Удаляем исключение

   pEx->Delete();

}

Чтобы прервать дальнейшее выполнение цикла загрузки файла мы записываем в переменную nReadCount нулевое значение. Затем удаляем исключение, вызывая для него метод Delete.

Данные, которые считаны из файла на сервере и находятся в буфере ptrBuffer, надо записать в файл fLocalFile на локальном диске компьютера. Для этого передаем буфер ptrBuffer методу Write и указываем, что надо записать в файл nReadCount байт. Напомним, что при достижении конца файла на сервере метод Read считывает остаток файла, размер которого может быть меньше размера буфера:

fLocalFile.Write( ptrBuffer, nReadCount );

Когда очередной блок из файла считан, увеличиваем значение линейного индикатора на одну позицию. За время загрузки файла линейный индикатор изменит свое значение от крайне левого до крайне правого:

// Увеличиваем значение на линейном индикаторе

m_LoadProgress.StepIt();

После того, как файл полностью получен, переводим линейный индикатор загрузки в начальное положение. Вызываем для этого метод SetPos с нулевым значением в качестве параметра:

// После окончания загрузки файла сбрасываем

// линейный индикатор

m_LoadProgress.SetPos( 0 );

Так как открытый файл на сервере FTP более нам не нужен, закрываем его, а затем удаляем из памяти соответствующий управляющий объект:

// Закрываем файл на сервере

iFile -> Close();

// Удаляем объект iFile

delete iFile;

Также закрываем и локальный файл, в который скопирован файл с сервера FTP:

// Закрываем файл на локальном компьютере

fLocalFile.Close();

Освобождаем буфер ptrBuffer, вызывая функцию free:

// Освобождаем буфер ptrBuffer

free( ptrBuffer );


Содержание раздела