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

       

Загрузка файла в фоновом режиме


Исходный вариант приложения FtpView и его модификация обладают еще одним очень неприятным недостатком. Во время загрузки файлов с сервера FTP интерфейс приложения блокируется практически полностью. Только по линейному индикатору, который используется в доработанном варианте  приложения мы можем судить о том, что оно все же работает.

В остальном складывается такое впечатление, что приложение “зависло”. Оно даже не перерисовывает свое окно и если временно переключится на другое приложение, окно которого перекроет окно FtpView, то изображение в окне FtpView будет испорчено. Только по окончании загрузки файла наше приложение спохватится и восстановит свое окно.

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

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

За основу мы взяли базовый вариант приложения FtpView, выполняющий загрузку файлов с сервера FTP при помощи метода GetFile. Мы постарались свести все изменения в исходных текстах приложения к минимуму. Вы должны будете только добавить три глобальные переменные, изменить метод FtpFileDownload класса CFtpViewDlg, и добавить функцию ThreadFileLoadProc.

Определение глобальных переменных добавьте в самом начале файла FtpViewDlg.cpp непосредственно после директив препроцессора. Все три новых глобальных переменных global_sFtpAddress, global_sFileName и global_sLocalFileName являются объектами класса CString. Эти переменные используются для передачи задаче, осуществляющей загрузку файла, адреса сервера FTP, пути каталога и имени исходного файла на сервере FTP, а также полного пути файла на диске локального компьютера в который будет записан файл с сервера:


//================== Глобальные переменные ===================

// Адрес сервера FTP

CString global_sFtpAddress = "";   

// Полный путь файла на диске локального компьютера

CString global_sFileName = "";     

// Путь каталога и имя файла на сервере FTP



CString global_sLocalFileName = "";

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

Адрес сервера FTP мы берем из глобальной переменной global_sFtpAddress, путь файла, который надо загрузить с сервера и его название - из переменной global_sFileName, а полный путь файла на диске локального компьютера, куда полученный файл должен быть записан - из переменной global_sLocalFileName:

//============================================================

// Задача, выполняющая загрузку файла с сервера FTP

//============================================================

UINT ThreadFileLoadProc(LPVOID param)

{

   //=========================================================

   // Инициализируем сеанс связи с Internet

   //=========================================================

   CFtpConnection*   m_FtpConnection;   // Сервер FTP

   CInternetSession* m_InternetSession; // Сеанс связи

   // Создаем сеанс связи с Internet, указываем в качестве

   // имени программы-клиента строку FtpProcess

   m_InternetSession = new CInternetSession("FtpProcess");

   // В случае ошибки отображаем сообщение и завершаем

   // задачу с кодом завершения 1

   if(!m_InternetSession)

   {

      AfxMessageBox("New Session Error", MB_OK);

      return 1;

   }

  

   // Инициализируем указатель m_FtpConnection

   m_FtpConnection = NULL;

   // Пытаемся соединиться с сервером FTP

   try

   {

      // Соединяемся с сервером FTP. Эта операция



      // может вызвать исключение CInternetException

      m_FtpConnection = m_InternetSession ->

         GetFtpConnection(global_sFtpAddress);

   }

   catch (CInternetException* pEx)

   {

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

      AfxMessageBox("GetFtpConnection Error");

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

      pEx->Delete();

      // Обнуляем указатель m_FtpConnection

      m_FtpConnection = NULL;

   }

   if(m_FtpConnection != NULL)

   {

      BOOL fResult;

      // Загружаем файл с сервера

      fResult = m_FtpConnection ->

         GetFile(global_sFileName.GetBuffer(MIN_LEN_BUF),

                 global_sLocalFileName.GetBuffer(MIN_LEN_BUF),

                 FALSE

         );

      // Закрываем соединение с сервером

      m_FtpConnection -> Close();

      delete m_FtpConnection;

      // После успешного завершения загрузки выводим сообщение

      if( fResult )

         AfxMessageBox("File load complited:" +

                        global_sFileName);

   }

   // Завершаем сеанс связи с Internet

   m_InternetSession -> Close();

   delete m_InternetSession;

   return 0;

}

Функция ThreadFileLoadProc выполняет загрузку файла с сервера FTP в файл на диске локального компьютера. Надо только заполнить глобальные переменные global_sFtpAddress, global_sFileName и global_sLocalFileName, а затем запустить функцию ThreadFileLoadProc как отдельную задачу. Для этого измените метод FtpFileDownload класса CFtpViewDlg как это показано ниже:

BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )

{

   BOOL fResult;

   CString sLocalFileName;

   // Определяем объект класса CFileDialog, представляющий

   // стандартную диалоговую панель Save As, в которой

   // по умолчанию выбрано имя файла sFileName

   CFileDialog mFileOpen(FALSE, NULL, sFileName);

   // Отображаем диалоговую панель Open и позволяем

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



   int result = mFileOpen.DoModal();

   // Проверяем как была закрыта диалоговая панель Open -

   // по нажатию кнопки OK или Cancel

   if(result == IDCANCEL)

   {

      // Если пользователь отказался от выбора файлов и

      // нажал кнопку Cancel отображаем соответствующее

      // сообщение и возвращаем значение FALSE

      AfxMessageBox("File not selected");

      return FALSE;

   }

   // Если пользователь нажал кнопку OK

   else if(result == IDOK)

   {

      // Записываем полный путь файла на диске локального

      // компьютера

      global_sLocalFileName = mFileOpen.GetPathName();

      // Определяем путь и имя файла, который надо загрузить

      // с сервера FTP

      global_sFileName = sCurentDirectory + "/" + sFileName;

      // Запоминаем адрес сервера FTP

      global_sFtpAddress = m_FtpAddress;

      // Запускаем новую задачу, определенную функцией

      // ThreadFileLoadProc

      AfxBeginThread( ThreadFileLoadProc, NULL,

                      THREAD_PRIORITY_NORMAL);

   }

   return fResult;

}

Мы не будем подробно описывать функцию ThreadFileLoadProc и новый вариант метода FtpFileDownload класса CFtpViewDlg. В них используются те же приемы, что продемонстрированы в методе FtpFileDownload класса CFtpViewDlg базового варианта приложения FtpView.


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