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

Недостатки приложения FtpView


У приложения FtpView, рассмотренном в предыдущем разделе, есть как минимум один большой недостаток. В то время когда идет загрузка файла с сервера FTP, ход этого процесса никак не отображается и приложение создает впечатление зависшей программы. Только те, кто использует внешний модем, могут догадаться о работе приложения по интенсивному или не очень интенсивному миганию индикаторов на его лицевой панели. Если компьютер оборудован внутренним модемом, вы в принципе также можете следить за его состоянием, если разместите в панели управления Windows 95 (или Windows NT версии 4.0) индикатор работы модема.

Этот недостаток нашего приложения обусловлен устройством метода GetFile класса CFtpConnection, так как он не предусматривает возможности извещения приложения о загрузке части файла. Поэтому вместо использования метода GetFile мы предлагаем воспользоваться методом OpenFile класса CFtpConnection и методом Read класса CInternetFile. Метод OpenFile открывает файл, расположенный на сервере, а метод Read позволяет считывать открытый файл по частям.

Кроме того, метод GetFile сразу записывает файл, полученный с сервера, в файл на локальном диске компьютера. Метод Read записывает полученный фрагмент файла с сервера во временный буфер. Это позволяет, например, обеспечить дополнительную обработку данных по мере их получения.

Доработаем приложение FtpView так, чтобы оно показывало ход загрузки файлов с сервера FTP. Для этого сначала загрузите в редактор ресурсов диалоговую панель IDD_FTPVIEW_DIALOG. Немного увеличьте вертикальный размер панели и добавьте внизу панели линейный индикатор progress bar, выбрав для него идентификатор IDC_PROGRESS, и текстовую строку Progress (рис. 2.13). Фрагмент файла FtpView.rc, содержащий шаблон диалоговой панели мы привели в листинге 4.4.

Листинг 2.13. Фрагмент файла FtpView.rc

//////////////////////////////////////////////////////////////

//

// Dialog

//

IDD_FTPVIEW_DIALOG DIALOGEX 0, 0, 352, 215

STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |


                      WS_SYSMENU



EXSTYLE WS_EX_APPWINDOW

CAPTION "FtpView"

FONT 8, "MS Sans Serif"

BEGIN

  PUSHBUTTON      "Connect",IDC_CONNECT,196,5,45,15

  PUSHBUTTON      "On top",IDC_ON_TOP,253,5,40,15

  DEFPUSHBUTTON   "OK",IDOK,305,5,40,15

  CONTROL    "List1",IDC_FTP_LIST,"SysListView32",LVS_REPORT |

              LVS_SORTASCENDING | LVS_ALIGNLEFT | WS_BORDER |

              WS_TABSTOP,5,25,340,133

  EDITTEXT    IDC_FTP_ADDRESS,5,5,183,14,ES_AUTOHSCROLL

  LTEXT       "Directory:",IDC_STATIC,7,171,33,8

  EDITTEXT    IDC_STATUS,44,169,301,15,ES_AUTOHSCROLL |

              ES_READONLY

  CONTROL     "Progress1",IDC_PROGRESS, "msctls_progress32",

              WS_BORDER, 43,193,302,14

  LTEXT       "Progress:",IDC_STATIC,7,196,30,8

END

Запустите MFC ClassWizard, перейдите на страницу Member Variables, выберите класс CFtpViewDlg и добавьте к нему управляющий объект для линейного индикатора. Для этого выберите из списка Control IDs идентификатор IDC_PROGRESS и нажмите кнопку Add Variable. Вам будет предложено привязать к линейному индикатору объект класса CProgressCtrl. Введите имя этого объекта. В нашем случае мы использовали имя m_LoadProgress. Нажмите кнопку OK и закройте MFC ClassWizard.

В классе CFtpViewDlg появится новый элемент m_LoadProgress. Он будет добавлен в блоке AFX_DATA, которым управляет MFC ClassWizard:

class CFtpViewDlg : public CDialog

{

// Construction

public:

   ~CFtpViewDlg();

   CFtpViewDlg(CWnd* pParent = NULL);

// Dialog Data

   //{{AFX_DATA(CFtpViewDlg)

   enum { IDD = IDD_FTPVIEW_DIALOG };

   CProgressCtrl   m_LoadProgress;

   . . .

   //}}AFX_DATA

   . . .

}

Кроме того, вносятся изменения в метод DoDataExchange класса CFtpViewDlg. В блоке AFX_DATA_MAP, в котором MFC ClassWizard размещает методы для обмена данными, добавляется новая строка. Она устанавливает связь между линейным индикатором IDC_PROGRESS и объектом m_LoadProgress:



DDX_Control(pDX, IDC_PROGRESS, m_LoadProgress);

Перед тем как использовать линейный индикатор, следует выполнить его настройку - установить границы значений в которых он может меняться и шаг приращения. В принципе, вы можете оставить эти значения по умолчанию. В этом случае линейный индикатор будет отображать значения в интервале от 0 до 100 с шагом 10. Однако мы все же выполним инициализацию линейного индикатора, чтобы программный код приложения был более нагляден.

Загрузите в текстовый редактор Microsoft Visual C++ метод OnInitDialog класса CFtpViewDlg и добавьте к нему вызовы методов SetRange и SetStep, как это показано на листинге 2.14. Обратите внимание, что эти методы вызываются для объекта m_LoadProgress, представляющего линейный индикатор IDC_PROGRESS.

Листинг 2.14. Фрагмент файла FtpViewDlg.cpp, метод OnInitDialog класса CFtpViewDlg

BOOL CFtpViewDlg::OnInitDialog()

{

   // Вызываем метод OnInitDialog базового класса CDialog

   CDialog::OnInitDialog();

   // Устанавливаем пиктограммы, которые будут отображаться

   // в случае минимизации диалоговой панели

   SetIcon(m_hIcon,TRUE);  // Пиктограмма стандартного размера

   SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера

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

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

   m_LoadProgress.SetRange(0, 100);

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

   m_LoadProgress.SetStep(10);

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

   // Выполняем инициализацию списка IDC_FTP_LIST

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

   . . .

}

Наибольшие изменения надо внести в метод FtpFileDownload класса CFtpViewDlg. Мы должны изменить способ загрузки файла с сервера - отказаться от использования метода GetFile и принимать файл по частям, используя методы OpenFile и Read. Так как в этом случае принимаемые данные не записываются в файл, а помещаются во временный буфер, необходимо организовать запись данных из буфера в файл на локальном диске. Для этого мы будем использовать возможности класса CFile. И наконец, по мере загрузки файла с сервера мы будем изменять состояние линейного индикатора IDC_PROGRESS.



Модифицированный метод FtpFileDownload класса CFtpViewDlg, выполняющий все описанные выше действия представлен в листинге 2.15.

Листинг 2.15. Фрагмент файла FtpViewDlg.cpp, метод 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;

   }

   else if(result == IDOK)

   {

      // Если пользователь нажал кнопку OK, определяем

      // имя выбранного файла

      sLocalFileName = mFileOpen.GetPathName();

   }

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

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

   // сервера FTP

   sFileName = sCurentDirectory + "/" + sFileName;

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

   // Чтобы узнать размер файла, записанного на сервере FTP,

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

   CFtpFileFind m_FtpFileFind(m_FtpConnection);

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

   if(fResult =

      m_FtpFileFind.FindFile(_T(sFileName)))

   {

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

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

      fResult = m_FtpFileFind.FindNextFile();

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



      DWORD dwLength = 0;

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

      dwLength = m_FtpFileFind.GetLength();

      // В соответствии с размером файла устанавливаем новые

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

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

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

      m_LoadProgress.SetStep(1);

   }

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

   m_FtpFileFind.Close();

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

   CInternetFile* iFile;

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

   iFile = m_FtpConnection -> OpenFile(

      sFileName.GetBuffer(MIN_LEN_BUF),

      GENERIC_READ,

      FTP_TRANSFER_TYPE_BINARY

   );

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

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

   CFile fLocalFile(

            sLocalFileName,

            CFile::modeCreate | CFile::modeWrite

         );

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

   // Временная переменная. В нее заносится количество байт,

   // считанное с файла на сервере

   UINT nReadCount = 0;

  

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

   void* ptrBuffer;

   ptrBuffer = malloc( READ_BLOCK );

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

   // файл и изменяем текущее положение линейного индикатора

   do

   {

      // Если во время операции чтения возникнет исключение

      // CInternetException, то оно будет обработано

      // соответствующим блоком catch

      try

      {

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

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

      }

      catch (CInternetException* pEx)

      {

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

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

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



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

            AfxMessageBox(szErr);

         else

            AfxMessageBox("GetFtpConnection Error");

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

         nReadCount = 0;

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

         pEx->Delete();

      }

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

      // которая находится в буфере в локальный файл

      fLocalFile.Write( ptrBuffer, nReadCount );

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

      m_LoadProgress.StepIt();

   // Продолжаем чтение файла с сервера до тех пор, пока не

   // будет достигнут конец файла

   } while (nReadCount == READ_BLOCK);

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

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

   m_LoadProgress.SetPos( 0 );

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

   iFile -> Close();

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

   delete iFile;

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

   fLocalFile.Close();

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

   free( ptrBuffer );

   return fResult;

}

Постройте проект и запустите полученное приложение. Введите адрес сервера FTP и нажмите кнопку Connect. Далее выберите из списка файлов на сервере, файл для загрузке и сделайте по нему двойной щелчок левой кнопкой мыши. Как и ранее, вам будет предложено указать имя файла на локальном компьютере, в который будет записан файл с сервера FTP. Затем начнется процесс загрузки файла, ход которого будет отображаться линейным индикатором в нижней части диалоговой панели приложения (рис. 2.16).



Рис. 2.16. Загрузка файла с сервера FTP

Как вы уже могли заметить, основные изменения в исходных текстах приложения FtpView коснулись только метода FtpFileDownload класса CFtpViewDlg, который собственно и осуществляет загрузку файла с сервера FTP. Рассмотрим подробнее как работает этот метод.


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