A Simple File Downloading Method with Progress Bar Updating

Environment: Compiled and tested on VC++ 5.0 and 6.0; running Windows 2000/NT/XP

Many people have wondered how to download a file from the Internet while having a progress bar to track the download progress. In this example, I will demonstrate how to achieve this task. The article also covers how to get file complete percentage, bytes received, bytes left, and download speed (kb/sec).

The pictures below are snapshots off the downloadable example in this article. The code is fully commented for easy understanding and I will cover every aspect of the program in this article in detail so that coders can understand and implement this code on any program they want.

        

Note: The methods I used are probably not the fastest and most efficient out there, but that is the way I code. I hope that when you finish reading this article, you will be able to write such a thing yourself...so let's get started!

General Description

The program uses a worker thread to perform the file download; this is so that our main application won't be blocked and so that we could have a cancel download functionality inside the program. All the downloading is being done from inside the thread along with the on-screen updating; we will get into that in a minute.

Coding Time!

Note: The following code is just a general overview of how to perform these tasks. Look inside the program source code to see the actual implementation of the code below:

CInternetSession netSession;
CStdioFile *file;

file = netSession.OpenURL(char url,1,INTERNET_FLAG_TRANSFER_BINARY
                                   | INTERNET_FLAG_RELOAD);

This function returns a pointer to a CStdioFile, which is currently the file we requested online. You now can process the file as it was on your local hard drive. I haven't tested writing into a file using this method but it probably will require the permission and access for the file write method on the specified server.

Reading from the File and Writing It to the Local Hard Drive

Reading from the file is quite easy; all we have to do is this:

file->Read(char buffer,int bytes to read);
CFile fl;
fl.Open("myfile",CFile::modeCreate | CFile::modeWrite
                                   | CFile::typeBinary);
fl.Write(buffer,bytes read);

Progress Bar, Transfer Data, and More

To make our progress bar effective, we use the CProgressCtrl::SetRange32(); function. The parameters passed to the function are lower value and upper value; lower value is of course 0 and the upper value is the file size, which we determine by simply calling:

int x = file->SeekToEnd();

The ::SeekToEnd(); function returns the total bytes of the file, which is determined into a file size.

The updating of the progress bar is being made every time we read a chunk of the file. Our program uses chunks of 512 bytes, not to overload the connection. The progress is updated every time the program performs another file->Read(); function.

while(int bytesread = file->Read(charbuf,512))
{
currentbytes = currentbytes + bytesread;
CProgressCtrl::SetPos(currentbytes);
}

To calculate our bytes received, percentages, and download speed(kb/sec), use the following code.

int percent = currentbytes * 100 / x ( our file size integer
                                       from above);
int kbreceived = currentbytes / 1024;

Calculating the download speed is a bit trickier. Before we go into the while loop, we create a COleDateTime object to get the current time and declare a variable (double) to store our seconds between the current time and the download start time.

COleDateTime dlStart = COleDateTime::GetCurrentTime();
double secs;

This function will get the time the download started. In the while loop, we declare a COleDateTimeSpan object to calculate the seconds between the download start and the current time.

COleDateTimeSpan dlElapsed = COleDateTime::GetCurrentTime()
                           - dlStart;
secs = dlElapsed.GetTotalSeconds();

Now "secs" holds the seconds between when we started downloading the file and now... dividing the seconds with the kbs received will result in the speed rate (kb/sec).

double kbsec = kbreceived / secs;

This is it, a general information on how to achive these tasks. The program also implements error handling by using the try and catch phrases to determine error messages and printing them out to the screen. Please refer to the source code for more information.

I hope this article helped you figure out everything you need to know about downloading a file from the Internet.

Downloads

Download project workspace - 47 Kb

IT Offers

Comments

  • Memory Leak !!!

    Posted by chgyver on 05/11/2004 04:28am

    If I click "CANCEL" Button as downloading...
    This prog. create memory leak msg...
    
    And you can solve this problem...
    Modify, AfxEndThread(0) ==> return 0;
    
    In DownloadFile(LPVOID pParam)
    ------------------------------------
    		if(nTerminate == 1) // See if we got a Cancel requesst
    		{
    			netSession.Close();
    			fDestFile.Close(); // Close our destination file
    			fTargFile->Close(); // Close remote file
    			delete fTargFile; // Delete the CStdioFile object to prevent leaks
    			fTargFile = NULL;
    			pwnd->SetDlgItemText(IDC_STATUS,"Download cancelled by user"); // Set satus bar text
    //			AfxEndThread(0); // End thread ==> MODIFY !!!
    			return 0;  // new code !!!
    		}

    Reply
  • Need a time out

    Posted by Herve3D on 04/13/2004 05:52am

    Sometimes during a file downloading, the program stay endlessly in this line : while(int bytesread = file->Read(charbuf,512)) I think we need to add a time out. Someone know a smart solution for this problem?

    Reply
  • memory exception

    Posted by Legacy on 02/09/2004 12:00am

    Originally posted by: Peter R.

    "delete fTargFile;" is executed twice in ProgressExDlg.cpp, so you need to remove the second one.

    delete fTargFile;
    if(nDownloaded == 1)
    {
    pwnd->SetDlgItemText(IDC_STATUS,"Download complete");
    delete fTargFile;
    }

    Reply
  • Memory Exception

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

    Originally posted by: Kapil Suri

    whenever i try to run it
    it gives memory exceptiion error
    waiting for any one rpz
    kapil231@rediffmail.com

    Reply
  • SeekToEnd didn't work

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

    Originally posted by: Yae Chan

    When i was using SeekToEnd fuction it doesn't work as i expected. Any, thank you.

    Reply
  • Exception Error (The Memory Could not be Read)

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

    Originally posted by: Sachin Mundhra

    When i run the application it gives Unhandled Exception.

    Sachin

    Reply
  • need help in resuming

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

    Originally posted by: jag

    can anybody help me in how to resume a download.

    thank u
    jag

    Reply
  • File Size without using SeekToEnd

    Posted by Legacy on 12/18/2002 12:00am

    Originally posted by: Steve Scanlon

    To obtain the size of the file to download without using
    
    SeekToEnd use CInternetSession::QueryInfo. SeekToEnd
    appears to require the server to perform random access
    whereas QueryInfo uses the HTTP header. This tested using a
    dial up line, this method gives true progress reporting.

    The extract from my code below differs from this excellent
    article by casting the return from OpenURL to a CHttpFile
    pointer rather than CStdioFile.


    DWORD FileSize;
    CHttpFile *RemoteFile;
    // open session
    CInternetSession NetSession(UserAgentString);
    // open the URL
    RemoteFile = (CHttpFile*)NetSession.OpenURL(LocationFileURL, 1,
    INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_RELOAD);
    // get the expected file size into a DWORD.
    RemoteFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH, FileSize);
    // read / write to disk loop
    while(Bytes = RemoteFile->Read(Buffer, 1024) )
    {
    .
    . // use FileSize and cumulative bytes read to show progress
    .
    }

    Reply
  • False Progress, Its Wrong

    Posted by Legacy on 11/25/2002 12:00am

    Originally posted by: Paul

    The Open function does the download. That means that your app will wait for the Open function to return. Once it returns the whole file is in cache. So you can read the file as if its on your hard drive because it is on your had drive. So the read function reads a local copy. Your progress bar will time how quickly your PC copies the file from cache to your destination instead of how quickly the file is read. Think about it, how can you seek the end of the file if you haven't donwloaded it yet.

    Try the code on a dialup modem. The status bar will sit idle for some time and then shoot across. The idle is the download. The shoot is the file copy. I think you have to overwrite the CInternetSession class to get a true progress.

    Hope this clears up some misconceptions.

    • asd

      Posted by killbill7 on 01/06/2008 06:59pm

      asd

      Reply
    Reply
  • Devide by zero bug

    Posted by Legacy on 11/07/2002 12:00am

    Originally posted by: BeomSeok, Lee

    This code includes devide by zero bug.
    (Line number about 202).

    Original code:
    pwnd->SetDlgItemText(IDC_KBSEC, KBsec);

    Debugged code:
    if(secs != 0) // Add code
    pwnd->SetDlgItemText(IDC_KBSEC, KBsec);

    Reply
  • Loading, Please Wait ...

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

Whitepapers and More

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds