29
Sep
2008

Daca va intereseaza sa descarcati fisiere de pe un server web, MFC pune la dispozitie o suita de clase pentru acest lucru. Acest articol prezinta un exemplu pentru descarcarea unui fisier folosind protocolul HTTP.

Dintre clasele importante din MFC amintesc:

  • CInternetSession: creaza si initializeaza una sau mai multe sesiuni. Pentru deschiderea unei conexiuni directe catre un anumit server, are cateva functii membre:
    • GetGopherConnection: pentru deschiderea unei conexiuni la un server gopher;
    • GetHttpConnection: pentru deschiderea unei conexiuni la un server HTTP;
    • GetFtpConnection: pentru deschiderea unei conexiuni la un server FTP;
  • CInternetConnection: o clasa de baza pentru managementul unei conexiuni la un server; urmatoarele clase sunt derivate din ea:
    • CFtpConnection: pentru o conexiune FTP permitand manipulare directa a fisierelor si directoarelor de pe server;
    • CHttpConnection: pentru o conexiune HTTP;
    • CGopherConnection: pentru o conexiune la un server Gopher;
  • CInternetFile: derivat din CStdioFile, permite accesul la fisiere de pe sisteme remote, care folosesc protocoale Internet. exista urmatoarele clase derivate:
    • CHttpFile: functionalitate pentru cererea si citirea fisierelor de pe un server HTTP;
    • CGopherFile: functionalitate pentru cererea si citirea fisierelor de pe un server gopher;
  • CInternetException: exceptie legata de o operatie la Internet;

Pentru a descarca un fisier de pe un server HTTP trebuie parcursi urmatorii pasi:

  • crearea unei sesiuni;
  • apelarea functiei AfxParseURL() pentru impartirea URL-ului pe componente, si verificarea ca acesta este unul formatat corect;
  • obtinerea unei conexiuni HTTP prin apelul functiei GetHttpConnection();
  • deschiderea conexiuni prin apelul functiei OpenRequest(), care in acest caz returneaza un pointer la un obiect CHttpFile.
  • verificarea starii cererii HTTP, prin apelul functiei QueryInfoStatusCode();
  • daca starea indica redirectarea locatiei, atunci conexiunea trebuie inchisa, si pasii de mai sus, incepand cu al doilea, trebuie reluati;
  • daca avem o conexiune directa functionala, atunci se poate citi fisierul cu Read() sau ReadString().

Acesti pasi se pot vedea in codul de mai jos. Am folosit o clasa derivata din CInternetException pentru a indica un cod de eroare si o clasa derivata din CInternetSession pentru update-ul starii (fara a face insa nimic).

bool CFileDownloader::Download(const CString& sURL, const CString& filepath)
{
	bool retval = false;

	DWORD dwHttpRequestFlags = INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_AUTO_REDIRECT; 
	
   // create a new internet session
	CCustomInternetSession session(
      _T("Demo download"),          // name
      PRE_CONFIG_INTERNET_ACCESS);  // access type

	CHttpConnection* pServer = NULL; 
	CHttpFile* pFile = NULL;
	
	try
	{
		CString strServerName; 
		CString strObject; 
		INTERNET_PORT nPort = INTERNET_INVALID_PORT_NUMBER; 
		DWORD dwServiceType = INTERNET_SERVICE_HTTP; 

		// parse the URL into components
		if (!AfxParseURL(sURL, dwServiceType, strServerName, strObject, nPort)) 
		{ 
			TRACE(_T("Error: URL could not be parsed")); 
			throw new CCustomInternetException(WDEX_ERROR_PARSING_URL);
		}

		// make sure we are using HTTP
		if(dwServiceType != INTERNET_SERVICE_HTTP)
		{
			TRACE(_T("Error: can only use URLs beginning with http://\n")); 
			throw new CCustomInternetException(WDEX_NOT_HTTP_PROTOCOL);
		}
		
		// enable status callback
		VERIFY(session.EnableStatusCallback(TRUE)); 

		// get a HTTP connection
		pServer = session.GetHttpConnection(strServerName, nPort);
		
		// create a HTTP file
		pFile = pServer->OpenRequest(
			CHttpConnection::HTTP_VERB_GET, 
			strObject, 
			NULL, 
			1, 
			NULL, 
			NULL, 
			dwHttpRequestFlags); 

		// make the request
		pFile->SendRequest(); 
		
		DWORD dwRet = 0; 
		pFile->QueryInfoStatusCode(dwRet); 
		
		// if access was denied, prompt the user for the password 
		if (dwRet == HTTP_STATUS_DENIED) 
		{
			throw new CCustomInternetException(WDEX_ACCESS_DENIED);
		}

		// test redirection
		if (dwRet == HTTP_STATUS_MOVED || 
			dwRet == HTTP_STATUS_REDIRECT || 
			dwRet == HTTP_STATUS_REDIRECT_METHOD) 
		{ 
			CString strNewLocation; 
			pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation); 
			
			int nPlace = strNewLocation.Find(_T("Location: "));
			if (nPlace == -1) 
			{ 
				TRACE(_T("Error: Site redirects with no new location\n")); 
				throw new CCustomInternetException(WDEX_REDIRECTION_NO_LOCATION);
			}
			
			strNewLocation = strNewLocation.Mid(nPlace + 10); 
			nPlace = strNewLocation.Find(_T('\n'));
			if (nPlace > 0) 
				strNewLocation = strNewLocation.Left(nPlace); 
			
			// close up the redirected site 
			pFile->Close();
			delete pFile; 

			pServer->Close(); 
			delete pServer; 
			
			// figure out what the old place was 
			if (!AfxParseURL(strNewLocation, dwServiceType, strServerName, strObject, nPort)) 
			{ 
				TRACE(_T("Error: URL could not be parsed")); 
				throw new CCustomInternetException(WDEX_ERROR_PARSING_URL);
			}
			
			// make sure we are using HHTP
			if(dwServiceType != INTERNET_SERVICE_HTTP)
			{
				TRACE(_T("Error: can only use URLs beginning with http://\n")); 
				throw new CCustomInternetException(WDEX_NOT_HTTP_PROTOCOL);
			}
			
			// try again at the new location 
			pServer = session.GetHttpConnection(strServerName, nPort); 
			
			pFile = pServer->OpenRequest(
				CHttpConnection::HTTP_VERB_GET, 
				strObject, 
				NULL, 
				1, 
				NULL, 
				NULL, 
				dwHttpRequestFlags); 

			pFile->SendRequest(); 
			
			pFile->QueryInfoStatusCode(dwRet); 

			if (dwRet != HTTP_STATUS_OK) 
			{ 
				TRACE(_T("Error: Got status code %d"), dwRet); 
				throw new CCustomInternetException(WDEX_INVALID_HTTP_STATUS);
			}
		} 

		CFile file;
		if(file.Open(filepath, CFile::modeCreate|CFile::modeWrite))
		{
			unsigned char buffer[1024] = {0};
			DWORD readbytes = 0;
			while((readbytes = pFile->Read(buffer, sizeof(buffer))) > 0)
			{
				file.Write(buffer, readbytes);
			}

			file.Close();
			retval = true;
		}
		else
		{
			TRACE("Error code: %d\n", GetLastError());
		}

		pFile->Close(); 
		pServer->Close();		
	} 
	catch(CCustomInternetException *pEx)
	{
		TRACE1("Error: Exiting with CTearException(%d)\n", pEx->GetErrorCode()); 
		pEx->Delete(); 
	}
	catch(CInternetException *pEx)
	{
		TCHAR szErr[1024]; 
		pEx->GetErrorMessage(szErr, 1024); 
		
		pEx->Delete(); 
	}

	if (pFile != NULL) 
		delete pFile; 
	if (pServer != NULL) 
		delete pServer;

	session.Close(); 

	return retval;
}
[phpBB Debug] PHP Warning: in file [ROOT]/phpbb/db/driver/mysqli.php on line 317: mysqli_free_result(): Couldn't fetch mysqli_result