Advertisement
2002C Internet/ Browsers/ HTML #16611

Download a file from the Internet reliably

In this article you will find out why you just can't call a few WinInet functions and expect them to download a file for you. You will learn the proper way to download a file from the Internet, and display its progress and download speed!

AI

AI Summary: This codebase represents a historical implementation of the logic described in the metadata. Our preservation engine analyzes the structure to provide context for modern developers.

Source Code
original-source
First, create a function to handle downloading of a file from the web. My example is like this:
<BR><B>
UINT InternetGetFile (HINTERNET IN hOpen, CHAR *szUrl, CHAR *szFileName, HWND hwndProgress, int idStatusText, int idProgressBar);<BR>
<BR></B>
It's UINT because it will return a value if an error occurs. Otherwise, it will return 0. In order to use this function, all you have to do is provide a valid HINTERNET handle obtained using a standard InternetOpen() call. If you want, you can provide a handle to a progress window (and an ID for the STATIC control for status, and the PROGRESS control for the progress bar), where the function will display its status. Everything else is self-explanatory.
I begin the body of the function declaring some variables.
<BR>
  <B>DWORD dwSize;<BR></B>
<BR>
This variable will be used to store how much data is read with every call to InternetReadFile.
<BR>
  <B>CHAR szHead[] = "Accept: */*\r\n\r\n";<BR></B>
<BR>
In this variable, the much-needed HTTP header is stored. If you do not pass this header onto the InternetOpenUrl call, it will only allow you to open text files!<BR>
  <B>VOID* szTemp[16384];<BR></B>
<BR>
This variable is a buffer in which 16 KB of data from the Internet file will be stored.
<BR>
  <B>HINTERNET hConnect;<BR></B>
<BR>
This is a HINTERNET handle containing the request result (from InternetOpenUrl).
<BR>
<B>
FILE * pFile;<BR>
</B>
<BR>
A standard C file handle(must include stdio.h). If you wish, you can use file APIs from Win32.
<BR>
<B>
if (!(hConnect = InternetOpenUrlA (hOpen, szUrl, szHead, lstrlenA (szHead), INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD, 0)))<BR>
  {<BR>
   return INTERNET_ERROR_OPENURL;<BR>
  }<BR></B>
<BR>
With this call, you open a handle to the Internet file using a URL. The flags indicate that the file will always be read from the Internet rather than the cache. If it fails, the function returns an error. You can give INTERNET_ERROR_OPENURL any value you like. You have to define all these errors in order for this function to work. You can also replace it with a number.<BR>
  <B>if(!(pFile = fopen(szFileName, "wb" )))<BR>
  {<BR>
   return INTERNET_ERROR_FILEOPEN;<BR>
  }<BR></B>
<BR>
With this call, an attempt is made to open a file with the given file name. If it fails, another custom-defined error is returned.
	<BR><B>DWORD dwByteToRead = 0;<BR>
	DWORD dwSizeOfRq = 4;<BR>
	DWORD dwBytes = 0;<BR></B>
<BR>
These three variables will store the size of the file, the size of the HttpQueryInfo content, and the number of bytes read in total, respectively.
  <BR>
<B>if (!HttpQueryInfo(hConnect, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwByteToRead, &dwSizeOfRq, NULL))<BR>
	 {<BR>
		dwByteToRead = 0;<BR>
	 }<BR>
</B>
<BR>
With this call, an attempt is made to get the file's size. If the attempt fails, the dwByteToRead variable is set to 0, and no percentage or total size is displayed when the file is downloaded.
<BR>
<B>
	DWORD start;<BR>
	DWORD end;<BR>
	DWORD time;<BR>
	time = 10;<BR>
	start = timeGetTime();<BR>
</B>
<BR>
For this bit, you will need to include mmsystem.h and link with the library winmm.lib, to allow these timing features. The timing features will tell the user how fast the download is. Using this example, only the Internet download speed will be stated. However, you can expand this functionality to estimate the time remaining as well.<BR>
<B>
  do<BR>
  {<BR>
   if (!InternetReadFile(hConnect, szTemp, 16384, &dwSize))<BR>
   {<BR>
   fclose (pFile);<BR>
   return INTERNET_ERROR_READFILE;<BR>
   }<BR>
</B>
<BR>
With this part, a loop begins in which the file is downloaded in 16 KB chunks. If the download request fails, the file is closed, and an error is returned.<BR>
<B>
   if (!dwSize)<BR>
    break;<BR>
   else<BR>
    fwrite(szTemp, sizeof(char), dwSize, pFile);<BR>
</B>
<BR>
If dwSize is 0, it means EOF has been encountered, so the loop exits. Otherwise, the contents of the data read by InternetReadFile are written to the local file.
	<BR><B>dwBytes+=dwSize;<BR>
	if(dwByteToRead && hwndProgress)<BR>
	{<BR>
	SendDlgItemMessageA(hwndProgress, idProgressBar, WM_USER+2, (dwBytes*100)/dwByteToRead, 0);<BR>
	UpdateWindow(hwndProgress);<BR>
	}<BR></B>
<BR>
With this code, dwBytes increases by the amount of data read from the file, and if the file's length is valid, and a progress window handle was specified, the progress bar is updated to indicate the download's progress.<BR>
<B>
	FLOAT fSpeed = 0;<BR>
	fSpeed = (float)dwBytes;<BR>
	fSpeed /= ((float)time)/1000.0f;<BR>
	fSpeed /= 1024.0f;<BR>
</B>
<BR>
With this bit of code, the speed of the download is calculated based on the time it took, and the amount of data read.<BR>
<B>
	if(hwndProgress)<BR>
	{<BR>
		char s[260];<BR>
		sprintf(s, "%d KB / %d KB @ %1.1f KB/s", dwBytes/1024, dwByteToRead/1024, fSpeed);<BR>
		SetDlgItemTextA(hwndProgress, idStatusText, s);<BR>
		UpdateWindow(hwndProgress);<BR>
	}<BR>
</B>
<BR>
With this code, the status text of the progress window is set to indicate the download size and the speed of the download.<BR><B>
	end = timeGetTime();<BR>
	time = end - start;<BR>
	if(time == 0)<BR>
	time = 10;<BR></B><BR>
The time is updated.<BR>
<B><BR>} // do<BR>
  while (TRUE);<BR>
</B>
<BR>
The loop ends.<BR><B>
  fflush (pFile);<BR>
  fclose (pFile);<BR>
  return 0;<BR>
}</B><BR>
And finally, our function ends, closing the file and flushing the buffer to the hard drive. It returns 0, meaning success.<BR>
And that's it! With this bit of code and knowledge you can finally download files from the Internet efficiently and reliably.
The full code example will follow.
Original Comments (3)
Recovered from Wayback Machine