Downloading files via FTP

Environment: Visual C++ 6.0

I wrote some classes that download ftp files with a status dialog. My classes use the internet service classes included in MFC CInternetSession, CFtpConnection, and CFileFind. The downloading is done in a seperate thread so that the cancel button can cancel a download operation.

I want to mention two things up front on using CInternetSession.

  1. When you created your App with AppWizard if you selected link to MFC librarys dynamically be sure to add wininet.lib to you project settings link tab. If you link statically you should not get any errors for unresolved externals.
  2. If you try a debug build of a CInternetSession enabled app on computer that does not contain a late version of Internet Explorer it will fail. Only bother trying release versions on other computers. If you have VC++ 6.0, debug builds will work on the programmers computer becuase VC++ 6.0 installation installs a late version of Internet Explorer.

There are three classes:
  • FTPDownload() creates the dialog, monitors the cancel button and starts the seperate downloading thread.
  • FTPDownLoadFiles() Does the actual downloading in a seperate thread.
  • CProgressDlg() Displays the percent complete and has cancel button.

I have included a zip file containing the three classes each in thier own component (.ogx) file. I did this to simplify adding these classes to your program and becuase the progress dialog resource is included. If you are not sure of how to add components through the component gallery read about it in VC++ 6.0 help. After the following implementation notes, I show code for the FTPDownload, and FTPDownLoadFiles classes. I do not show the code for CProgressDlg because this code is pretty straight forward and the size of this article would be much larger. To see the code for CProgressDlg just download the zip file.

The only thing you need to know about to use these classes is the FTPDownload constructor.

FTPDownload(CStringArray * t_Csa, DWORD ctout);//Constructor

To use pass a CStringArray * to the constructor

This CStringArray has certain important CStrings in each position.
Csa[0] == Server Name
Csa[1] == User Name
Csa[2] == Password
Csa[3] = Ftp file name
Csa[4] = Local file name (name on local drive after downloaded)

As many Files as desired can be downloaded. Just begin filenames at index 3. File names are to be stored in alternating order, first the ftp file name then the local file name An optional second argument specifies the connection timeout value. In my opinion 3000 seemed the best. The default argument timeout of 0 will not change any timeout value in CInternetSession.

EXAMPLE:


//***************************ONDOWNLOADFILES*****************************
#include "FTPDownload.h"
void CMainFrame::OnDownloadFiles() 
{
    CStringArray Csa;
    //Lets use SetSize to prevent memory fragmentation and for efficiency
    Csa.SetSize(0,7);//We want at most 4 file names + first 3 CStrings stored in the C String Array Csa
    Csa.Add(server_name);//Csa[0] holds server name
    Csa.Add(user_name);//Csa[1] == user name
    Csa.Add(pass_word);//Csa[2] == pass word
    
    //Filenames are to be stored in alternating order.
    //First the ftp file name then the local file name (what the file should be named after downloaded)
    Csa.Add(ftp_fname1);//ftp file name
    Csa.Add(fname1);//then local file name
    Csa.Add(ftp_fname2);//ftp file name
    Csa.Add(fname2);//the local file name
    
    FTPDownload dl(&Csa, connection_timeout);//connection_timeout is optional
    m_wndStatusBar.SetPaneText(0, dl.result_str);//Note that the result_str will contain text to display
    //in the status bar (or wherever you want) after the download is complete.
    return;	
}

THE CLASSES----------------------------------------------------------------
FTPDownload CLASS HEADER FILE-----------------------------------------
#ifndef G_FTPDOWNLOAD_H
#define G_FTPDOWNLOAD_H

#include <afxinet.h>
#include "ProgDlg.h"
//This class handles the the progress dialog, and then launches a seperate thread to handle downloading files.
class FTPDownload
{
public:
    unsigned int thread_finished_flag;
    unsigned int abort_flag;
    CString result_str;
    CProgressDlg cpd;
    
    CStringArray * Csa;
    DWORD connection_timeout;
    FTPDownload(CStringArray * t_Csa, DWORD ctout = 0);//Constructor
};
#endif

FTPDownload CLASS CPP FILE-----------------------------------------
#include "stdafx.h"
#include "FTPDownload.h"
#include "FTPDownLoadFiles.h"

UINT DownLoadFunction(LPVOID lParam);//Global Prototype for thread function

//This class handles the progress dialog, and then launches a seperate thread to handle
//downloading files.
//**********************Default CONSTRUCTOR*********************
FTPDownload::FTPDownload(CStringArray * t_Csa, DWORD ctout)
{
    Csa = t_Csa;
    connection_timeout = ctout;
    result_str ="";
    abort_flag = FALSE;
    thread_finished_flag = FALSE;//When this == TRUE the other thread is finished with its job
    
    cpd.Create();//Create the progress dialog box.
    
    CWinThread * dl_thread = AfxBeginThread(::DownLoadFunction, this);//Start downloading thread
    
    while (thread_finished_flag == FALSE)
        if (cpd.CheckCancelButton()) abort_flag = TRUE;//If cancel pushed let other thread know it's time to split
        
        if (result_str.IsEmpty()==FALSE) return;//Then an exception was thrown and caught in the other thread!!
        if (abort_flag) result_str = "User Cancelled Download Operation";//Now set up result string
        else result_str = "Internet Download Completed Successfully";
        return;
}

//NOT A MEMEBER FUNCTION !!!!!
//This is the seperate thread function
//****************************DOWNLOADFUNCTION***********************************
UINT DownLoadFunction(LPVOID lParam)
{
    FTPDownload * pFtpI = (FTPDownload *)lParam;//I want a try block to catch any exceptions
    try	{	FTPDownLoadFiles dl(pFtpI);		}//This performs the downloading
    catch(char * str)//If a file can't be opened this is thrown
    { 		pFtpI->result_str = str; 		}
    catch (CFileException * pEx)
    { 		pFtpI->result_str.Format("File Error %d %s", pEx->m_cause, pEx->m_strFileName); 		}
    catch (CInternetException* pEx)
    {
        pFtpI->result_str.Format("Internet Exception Error %d", pEx->m_dwError);
        if (pEx->m_dwError == ERROR_INTERNET_EXTENDED_ERROR)
        {
            char temp_str[1024];
            DWORD length = 1024;
            DWORD temp_int = 0;
            ::InternetGetLastResponseInfo(&temp_int, temp_str, &length);
            pFtpI->result_str += temp_str;
        }
    }
    
    pFtpI->thread_finished_flag = TRUE;//Let the main thread know we finished
    return 0;//Don't care about return value
}

FTPDownLoadFiles CLASS HEADER FILE-----------------------------------------
#ifndef G_FTPDOWNLOAD_FILES_H
#define G_FTPDOWNLOAD_FILES_H

#include "FTPDownload.h"

//CInternetSession can throw an exception catch it like so: catch (CInternetException* pEx)

class FTPDownLoadFiles
{
protected:
    CInternetSession Cis;//All argument defaults are OK.
    CFtpConnection* m_Ftp_Conn;//The ftp_connection
    
    FTPDownload * pFtpI;//Pointer to FTPDownload object in other thread that also holds dialog
    CFile cfo;//CFileObject used to write file
    
    void ReadFile(CString & source, CString & dest);
    BOOL ftp_file_exists(CString & source);
    void file_not_found(CString & source);
    
    void UpdateStatus(void);
    char status_str[1024];
    
    unsigned int file_size;
    CString temp_ftp_name;
    
public:
    FTPDownLoadFiles(FTPDownload * t_pFtpI);//Constructor
};

#endif

FTPDownLoadFiles CLASS CPP FILE-----------------------------------------

#include "stdafx.h"
#include "FTPDownLoadFiles.h"

#define DL_BUFFER_SIZE 4096

//**********************CONSTRUCTOR*********************
FTPDownLoadFiles::FTPDownLoadFiles(FTPDownload * t_pFtpI)
{//CinternetSession is contained and is created with defaults
    pFtpI = t_pFtpI;
    m_Ftp_Conn = 0;
    //I have found that by reducing the timeout connection, the internet connection speed is faster.
    if (pFtpI->connection_timeout)//Only if not 0 do we bother changing the timeout value
        Cis.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, pFtpI->connection_timeout);//I like 3000
    
    int csa_size = pFtpI->Csa->GetSize();//Get the CStringArray size
    for (int i = 3;i<csa_size;i+=2)//start at index 3 past logon info
        ReadFile(pFtpI->Csa->GetAt(i), pFtpI->Csa->GetAt(i+1));//Download file name, disk file name
    
    Cis.Close();//Close this session
    return;
}

//m_pConn must be set B4 calling this function
//*********************READFILE*****************************
void FTPDownLoadFiles::ReadFile(CString &source, CString &dest)
{
    if (pFtpI->abort_flag) return;//Has cancel been pressed ? If yes then split!!!
    
    pFtpI->cpd.SetHeader(source);
    //Unfortunately we have to reopen the ftp connection for each file.
    wsprintf(status_str, "Connecting to %s", pFtpI->Csa->GetAt(0)); UpdateStatus();
    if (pFtpI->abort_flag) return;//Make sure operation hasn't been cancelled yet b4 calling inet function
    m_Ftp_Conn = Cis.GetFtpConnection(pFtpI->Csa->GetAt(0),
        pFtpI->Csa->GetAt(1), pFtpI->Csa->GetAt(2));//Connect to FTP Server
    
    strcpy(status_str, source); UpdateStatus();//Show source file name
    
    if (ftp_file_exists(source)==FALSE) return;//If file ain't there we can't download it so split!!
    pFtpI->cpd.SetUpper(file_size);
    
    if (cfo.Open(dest, CFile::modeCreate | CFile::modeWrite, NULL)==FALSE)//Now open our disk file
    {wsprintf(status_str, "Unable to create file %s", dest); AfxMessageBox(status_str); throw status_str; }
    
    pFtpI->cpd.SetHeader(temp_ftp_name);
    wsprintf(status_str, "Opening %s", temp_ftp_name); UpdateStatus();
    if (pFtpI->abort_flag) return;//Make sure operation hasn't been cancelled yet b4 calling inet function
    CInternetFile*  ifp = m_Ftp_Conn->OpenFile(temp_ftp_name);
    
    char buffer[DL_BUFFER_SIZE];
    unsigned int amount_read = DL_BUFFER_SIZE;
    unsigned int total_read = 0;
    while (amount_read == DL_BUFFER_SIZE && pFtpI->abort_flag == FALSE)
    {
        amount_read = ifp->Read(buffer, DL_BUFFER_SIZE);
        cfo.Write(buffer, amount_read);//Write this to our data file
        total_read += amount_read;
        wsprintf(status_str, "%d of %d  bytes read", total_read, file_size); 	UpdateStatus();
        pFtpI->cpd.SetPos(total_read);
    }
    
    cfo.Close();//Close the file
    //Unforunately we have to close the FTP session in order to be able to change to root folder.
    //There is no way around it. We have reopen the ftp connection for each file. Oh well.
    wsprintf(status_str, "Closing connection to %s", pFtpI->Csa->GetAt(0)); UpdateStatus();
    m_Ftp_Conn->Close();
    delete m_Ftp_Conn;//Delete the ftp connection just to be safe
    return;
}

//*********************************UPDATESTATUS***********************************
void FTPDownLoadFiles::UpdateStatus(void)
{
    pFtpI->cpd.SetStatus(status_str);
    return;
}

//This function performs three important functions.
//I have learned that calling OpenFile() on an FTP server when the files isn't there makes my
//program go BOOM so we have to check to see if the file is there first. I have also learned
//that in checking for the files existence we can also get the file size.

//1. It checks to see wether file exists on ftp server. If it doesn't it returns FALSE.
//2. Using CFtpFileFind it gets the FIRST file name matching input string. This means that if
//a string like amex*.txt is passed the FIRST and only the first file matching that string will
//be downloaded. This suits my purposes (for now) because I have to download daily data from a folder
//that is updated daily with the latest data file. This file name changes according to the date with
//the first four leters being the same. Using this logic you can still pass a specific file name
//or use wilcards, but just remember that if you use wildcards only the first file matching will
//be downloaded.

//3. Gets file size. we cannot however depend on this being correct. I read something about headers
//on the FTP site having to be updated, and also that CERN proxys can't get the file info or something
//Since this is the case and if it is less than DL_BUFFER_SIZE I just make it 0. If it is 0
//the progess dialog knows to ignore the percent stuff.

//This function sets temp_ftp_name. Use this name for the actual download.
//This function also sets file_size.
//*****************************FTP_FILE_EXISTS******************************
BOOL FTPDownLoadFiles::ftp_file_exists(CString &source)
{
    wsprintf(status_str, "Getting File Information %s", source); UpdateStatus();
    
    if (pFtpI->abort_flag) return FALSE;
    CFtpFileFind finder(m_Ftp_Conn);
    
    //Lets check for the files existince first using a standard FindFile call
    if (pFtpI->abort_flag) return FALSE;
    if (finder.FindFile(source) == FALSE)
    { 	file_not_found(source);	return FALSE;		}	
    
    //We have to use a FindNextFile to get info on first file, why ?. Becuase FindNextFile doesn't get the
    //next file on its first invocation. It gets the first file! Makes sense doesn't it? Pure Genius...
    if (pFtpI->abort_flag) return FALSE;
    finder.FindNextFile();
    temp_ftp_name = "";//Empty the CString
    temp_ftp_name = finder.GetFilePath();//Get the actual file name in case wildcards were used
    if (temp_ftp_name.IsEmpty()) temp_ftp_name = source;//Make sure we got something. If not use source
    file_size = 0;
    file_size = finder.GetLength();
    if (file_size < DL_BUFFER_SIZE) file_size = 0;//This tells the progress dialog to ignore the progress.
    
    return TRUE;//If here file definitely exists
}

//***************************FILE_NOT_FOUND*************************
void FTPDownLoadFiles::file_not_found(CString & source)
{
    wsprintf(status_str, "File NOT found on Server: %s", source);
    UpdateStatus();
    AfxMessageBox(status_str);//Put up message box to let user Know!!
    return;
}

Download demo project - 23.1 Kb



Comments

  • Plancha ghd rosa de 2010, por lo que siempre será amado por los usuarios de ghd

    Posted by mltowv442 on 07/17/2013 06:06pm

    Hoy en dia, hay muchas herramientas para que podamos hacer nuestro cabello más style.men la plancha ghd es nuestra mejor opción, ya que puede tener el pelo más sano y fashion.I sugieren se lava el cabello y secarlo completamente antes de uso de alisadores. Si usted utiliza el pelo o cualquier otro planchas de pelo, causarán daños reparables para el cabello que está húmeda o no completamente tør.Vi ofrecen muchos tipos de Babyliss baratos GHD, marca de plancha de pelo ghd baratas, nueva llegada, descuento planchas ghd, etc nuevos estilos, buen precio, varios colores, entrega rápida, orden de la pequeña cantidad accepteret.Efter mi opinión, que vuelve a alegrar por la noticia de que ghd está preparado para you.All la ghd es muy chi y barato con una calidad superior. son absolutamente el mejor producto en el mercado. [url=http://planchaspeloghdes.qsite.dk/]planchas GHD España[/url] He descubierto hace poco y he visto Palma Glam con mis propios ojos que no Danmarklancerer 2008 Edición limitada Purple GHD IV Styler Nueva Año Set.GHD IV Mark4 es ​​la marca líder mundial de planchas para el pelo año fladjern.Nye llevar a cabo una nueva edición limitada set de regalo, y este año hay planchas anderledes.ghd están en camino! Cuenta con estilos innovadores y muchos colores. y todos los productos expuestos en nuestra página web están disponibles y tienen las poblaciones here.We ofrecen precios muy especiales para nuestros clientes, y planchas ghd muy rápidas y seguridad son levering.Vores venta en descuento ahora, algunos de ellos son hasta un 44% de descuento . podemos entrega dentro de 24 horas después de recibir su payment.It es muy seguro puerta a puerta, no tener problemas con la aduana. [url=http://ghdchheapoutlet.moonfruit.com/]GHD España[/url] Somos pelo ghd planchas profesionales proveedores, tenemos muchos años de experiencia en la venta ghd. La comprensión de un gran número de personas pueden comprar problemas de productos. Más profesional para responder a sus preguntas. Hasta que esté satisfecho. Nuestra paciencia y cuidado, esto te hará sentir como si compra ghd es el más valioso. Puesto que vendemos muchos tipos de planchas ghd, y a todo color. Tipo que usted elija, tomar decisiones precisas para que no causan el mismo para usted. La mayoría de la gente se preguntará cómo se parece el precio? You Responder en este momento, nuestro precio es el más barato, usted puede buscar a través de, planchas GHD pertinentes para encontrar más tiendas, compara Internet to.du encontrará que nuestros productos son bedste.vi tiene un montón de ventajas para usted. Estamos aquí para comprar sus planchas de pelo ghd, se puede disfrutar de envío gratis. Si usted necesita preocuparse, ponemos a su disposición con los otros dos métodos. Para ir rápidamente a las manos. Cuando usted compra, usted no debe utilizar este producto, nos enseñará el método utilizado. Para ofrecerle un servicio de alta calidad. Si no te gusta para comprar productos, para que puedas ver nuestra política. Para el procesamiento. Planchas ghd en el producto, si usted tiene preguntas adicionales, por favor póngase en contacto con nosotros. Gracias por su apoyo y aliento de nuestros productos, creemos que la elección de planchas ghd, que son una hermosa opción.

    Reply
  • GHD ikke kunne uden brug af støtten bestÃ¥r af hÃ¥r alligevel national varenkangodt curls lÃ¥se

    Posted by motherdhmm on 05/30/2013 04:53pm

    [url=http://www.buy-beatsdrdre.com/]beats by dre headphones[/url] En ideel kvinde er ofte afbilledet til at være en person med en slank figur med langt hår. Hår har altid været vigtigt for hver kvinde, fordi det en eller anden måde afspejler deres personlighed. Have lange skinnende hår kan forføre en mand, kan det lander en kvinde i en shampoo kommercielle, eller få komplimenter fra andre. Det kan afspejle en kvindes sundhed og giver et indtryk af, at hun er sund, og at hun tager sig godt af sig selv. Selvfølgelig ville det ikke alle kvinder vil have langt hår. [url=http://www.blog.cheapbeatsbydre.co.nz/beats-by-dre-headphones]beats by dre headphones[/url] Nu skal vi starte det nye år. Du kan finde modeverdenen. Frisk stil model, har en god GHD håret dyrebar gave ting, så du skal være en god emne at diskutere med andre for at sikre, at alle kan diskutere, jeg ønsker at tale om dig. [url=http://www.buy-beatsdrdre.com/category/monster-beats-australia]monster beats australia[/url] Din særskilt web-site kan være din person i den vigtigste form for bare at leve netto web-sites inden for de britiske øer denne særlige tilbyder en utrolig mængde af krøllet hår behandling metode variabler ved hjælp af en række forskellige producenter. Disse former for god hår dag krøllet hår produkter igennem flere situationer tilbydes i form af faldende gebyrer. Denne unikke God hår dag Power Suppressor er normalt normalt en stærk tilstrækkeligt godkendt vilde hår terapi om at samle meget følsom med hinanden med sårede Lokker.

    Reply
  • how to resuem a download ?

    Posted by Legacy on 10/19/2003 12:00am

    Originally posted by: sha

    hi friends ,
    Is there any bodey who can tell me ,
    how to resuem a download ?
    I use VC++ and Cinternet session class .


    bye ,sh

    Reply
  • caught exceptions must be Delete()'d !

    Posted by Legacy on 08/09/2003 12:00am

    Originally posted by: Dave

    Any MFC CException or derived exception that is caught by a C++ exception, as is the case in this example's code, must have it's Delete() member function called prior to going out of scope. Otherwise, it will leak memory.

    Reply
  • DownLoading A whole Website

    Posted by Legacy on 08/02/2003 12:00am

    Originally posted by: nko

    ****o.,
    I've wrote A NT Service Program which DownLoad the Whole Website(all Link) From Internet.I've Big Problem in Internet COnnection which Connection Break Down,My Program is Can't Pending and Abort.And Connection is ReConnect,DownLoad From Starting Page.
    How to Pending on Current Page while Connection is Break-Down and Resuming From Last Page.
    Please Help me....If U Can...
    I'm Using MFC....
    Thank U so Much

    Reply
  • GSM/GPS & FTP Code Needed

    Posted by Legacy on 05/28/2003 12:00am

    Originally posted by: J.Somasundaram


    Presently iam working for an project which uses the GSM/GPS Module. What i have to do is
    1) Switch on the GSM & activate the GSM as an MODEM.
    2) Now communicate with an FTP / any server & put / get some datas from that.
    3) End up the connection.
    4) When called or when the main server is sending some datas to my GSM module, I have to get those datas & store in the local file.
    Pls help me to get the coding as soon as possible, as the time is running out for me. I would be very greatful if you can send the codings in the EVC++ or Likely in EVB.

    So If you can help me in getting the code for FTP /GSM / GPS pls mail me the source Code. I need them Very Urgently

    Waiting for your reply.
    Regds,
    Somasundaram.J
    somu@softcrylic.com

    Reply
  • how can i resume the download

    Posted by Legacy on 03/20/2003 12:00am

    Originally posted by: s.sreedhar


    the download manager download files , but how can i download multiple files sequentially?. also how can i resume a stopped download or resume a partially downloaded file?.
    from
    sreedhar

    Reply
  • Download Multiple Files, (simple but secure)

    Posted by Legacy on 03/13/2003 12:00am

    Originally posted by: Fagan Nicola

    BOOL C3GServerApp::FTP_GetFiles()
    {
    BOOL bValid = TRUE;

    CString szSrv = AfxGetApp()->GetProfileString(_T("FTP"), _T("Srv"), _T(""));
    CString szUser = AfxGetApp()->GetProfileString(_T("FTP"), _T("User"), _T(""));
    CString szPwd = AfxGetApp()->GetProfileString(_T("FTP"), _T("Pwd"), _T(""));

    try
    {
    //assumes server and file names have been initialized
    CInternetSession session("FTP Session");

    CFtpConnection* pConn = NULL;

    pConn = session.GetFtpConnection(szSrv, szUser, szPwd );

    CFtpFileFind oFind(pConn);

    BOOL bWorking = oFind.FindFile(_T("./MD*.TXT"));

    while(bWorking)
    {
    bWorking= oFind.FindNextFile();

    CString szNomeFile = oFind.GetFileURL();
    int nSl = szNomeFile.ReverseFind('/');
    CString szFileGet = szNomeFile.Mid(nSl+1);

    if (!pConn->GetFile(_T("./") + szFileGet, _T("C:\\3GServer\\") + szFileGet, FALSE, FILE_ATTRIBUTE_ARCHIVE, FTP_TRANSFER_TYPE_BINARY))
    {
    bValid = FALSE;
    delete pConn;
    }
    }


    session.Close();
    }
    catch (CInternetException * e)
    {
    e->Delete();
    bValid = FALSE;
    }

    return bValid;
    }

    Bye :)

    Reply
  • resume download too

    Posted by Legacy on 02/13/2003 12:00am

    Originally posted by: tressol ludovic

    if anyone could help me , i have to made a resume ftp download client. thx for the help .. just want the headr of function that do the resume ( CInternetFile::seek don't work ?->Only with HTTP connection ;o(

    Reply
  • resume download

    Posted by Legacy on 02/08/2003 12:00am

    Originally posted by: khaled shwareb

    hi groupe
    
    

    I made application using FTP but i can't add
    resume of download function or upload in my app.
    please help me for make it.


    thank you for your attention


    khaled

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Ever-increasing workloads and the challenge of containing costs leave companies conflicted by the need for increased processing capacity while limiting physical expansion. Migration to HP's new generation of increased-density rack-and-blade servers can address growing demands for compute capacity while reducing costly sprawl. Sponsored by: HP and Intel® Xeon® processors Intel, the Intel logo, and Xeon Inside are trademarks of Intel Corporation in the U.S. and/or other countries. HP is the sponsor …

  • You probably have several goals for your patient portal of choice. Is "community" one of them? With a bevy of vendors offering portal solutions, it can be challenging for a hospital to know where to start. Fortunately, YourCareCommunity helps ease the decision-making process. Read this white paper to learn more. "3 Ways Clinicians can Leverage a Patient Portal to Craft a Healthcare Community" is a published document owned by www.medhost.com

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds