c++实现下载文件


我们需要使用的WinInet API函数,调用顺序基本上是从上到下,在使用这些函数时,必须严格区分它们使用的句柄。这些句柄的类型是一样的,都是HINTERNET,但是作用不同,这一点非常让人迷惑。按照这些句柄的产生顺序和调用关系,可以分为三个级别,下一级的句柄由上一级的句柄得到。

  InternetOpen是最先调用的函数,它返回的HINTERNET句柄级别最高,我习惯定义为hSession,即会话句柄。

  InternetConnect使用hSession句柄,返回的是http连接句柄,我把它定义为hConnect。

  HttpOpenRequest使用hConnect句柄,返回的句柄是http请求句柄,定义为hRequest。

  HttpSendRequest、HttpQueryInfo、InternetSetFilePointer和InternetReadFile都使用HttpOpenRequest返回的句柄,即hRequest。

  当这几个句柄不再使用是,应该用函数InternetCloseHandle把它关闭,以释放其占用的资源。

由于,我是封装好的类,

下面,我们来看看.h文件,这个文件里面声明,变量,函数,对象。

#pragma once

#include  
#include
#define HTTPGET_BUFFER_MAX 1024
#define  DOWNHELPER_AGENTNAME         "MyAppByMulinB" 
#define  LEN_OF_BUFFER_FOR_QUERYINFO  128 
#define  DOWNLOAD_BUF_SIZE            (10*1024)  //10KB 
#define  MAX_DOWNLOAD_REQUEST_TIME    10   
#define  MAX_DOWNLOAD_BYTESIZE        (1000*1024*1024) //1000MB 
#define MAXSIZE 1024
class THttpGetThread
{
 //定义变量 
 HINTERNET hInet; //打开internet连接handle 
 HINTERNET hConnect; //HTTP连接 
 HINTERNET hRequestHead; //HTTP Request 
 HINTERNET hRequestGet; //HTTP Request 
 HANDLE hFileWrite = NULL; //写文件的句柄 
 DWORD dwDownBytes; //每次下载的大小 
 DWORD dwDownFileTotalBytes; //下载的文件总大小 
 DWORD dwWriteBytes; //写入文件的大小 
 char  bufQueryInfo[LEN_OF_BUFFER_FOR_QUERYINFO] ; //用来查询信息的buffer 
 DWORD dwBufQueryInfoSize ;
 DWORD dwStatusCode;
 DWORD dwContentLen;//获取文件大小
 DWORD dwSizeDW ;//文件大小变量大小
 WCHAR lpszFileName[INTERNET_MAX_HOST_NAME_LENGTH];//本地储存路径
 //分割URL 
 CHAR pszHostName[INTERNET_MAX_HOST_NAME_LENGTH] ;
 CHAR pszUserName[INTERNET_MAX_USER_NAME_LENGTH] ;
 CHAR pszPassword[INTERNET_MAX_PASSWORD_LENGTH] ;
 CHAR pszURLPath[INTERNET_MAX_URL_LENGTH]  ;
 CHAR szURL[INTERNET_MAX_URL_LENGTH]  ;
 URL_COMPONENTSA urlComponents  ;//创建结构体
 BOOL bRet;//返回值
 std::string strUrl;//url地址
public:
 THttpGetThread(char *, char *);
 ~THttpGetThread();
 BOOL Initialization();//初始化
 BOOL analysisURL();//解析URL
 void ReleaseResources();//释放资源
 BOOL CreateFile();//创建打开文件
 BOOL FileExists(LPCWSTR );//查看文件是否存在
 void Download();//下载
 void execute();//执行
 BOOL _TryHttpSendRequest(LPVOID , int );//尝试发送请求
};

那么,接着,我们看看.cpp文件

#include "stdafx.h"
#include "THttpGetThread.h"
#include
#include
#include
#include
#define  DOWNHELPER_AGENTNAME         "MyAppByMulinB"
#pragma comment(lib, "wininet.lib") 
//构造
THttpGetThread::THttpGetThread(char *url, char *path){
 strUrl.assign(url);//赋值给结构体的URl地址
 MultiByteToWideChar(CP_ACP, 0, path, strlen(path) + 1, lpszFileName,
  sizeof(lpszFileName) / sizeof(lpszFileName[0]));//赋值下载路径
}

THttpGetThread::~THttpGetThread()
{
}
//解析URL//填充结构体
BOOL THttpGetThread::analysisURL(){

 urlComponents.dwStructSize = sizeof(URL_COMPONENTSA);//这个结构体的大小,以字节为单位。
 urlComponents.lpszScheme = NULL;//指向包含该计划名称的字符串的指针。
 urlComponents.dwSchemeLength = NULL; //指向包含该计划名称的字符串的指针的长度
 urlComponents.nScheme = INTERNET_SCHEME_FTP;//internet_scheme值表明互联网协议方案。
 urlComponents.lpszHostName = pszHostName;//指向包含主机名的字符串的指针。
 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;//主机名长度
 urlComponents.nPort = NULL;//转换后的端口号
 urlComponents.lpszUserName = pszUserName;//用户名
 urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;//用户名长度
 urlComponents.lpszPassword = pszPassword;//指向包含密码的字符串的指针。
 urlComponents.dwPasswordLength = INTERNET_MAX_PASSWORD_LENGTH;//密码的大小,在TCHARs。
 urlComponents.lpszUrlPath = pszURLPath;//指向包含网址路径的字符串的指针。
 urlComponents.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;//URL路径的大小,在TCHARs。
 urlComponents.lpszExtraInfo = NULL;//指向一个包含额外信息的字符串的指针(例如?什么东西#)。
 urlComponents.dwExtraInfoLength = NULL;//指向一个包含额外信息的字符串的指针的长度(例如?什么东西#)。

 bRet = InternetCrackUrlA(strUrl.c_str(), 0, NULL, &urlComponents);//破解一个网址到它的组成部分。
 bRet = (bRet && urlComponents.nScheme == INTERNET_SERVICE_HTTP);//查看协议
 if (!bRet)
 {
  return false;
 }
 return true;
}
//初始化
BOOL THttpGetThread::Initialization(){
 //初始化
 hInet = InternetOpenA(DOWNHELPER_AGENTNAME, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL);
 if (!hInet)
 {
  return FALSE;  
 }
 //打开HTTP连接 
 hConnect = InternetConnectA(hInet, pszHostName, urlComponents.nPort, pszUserName, pszPassword, INTERNET_SERVICE_HTTP, 0, NULL);
 if (!hConnect)
 {
  return FALSE;
 }

 //创建HTTP request句柄 
 if (urlComponents.dwUrlPathLength != 0)
  strcpy(szURL, urlComponents.lpszUrlPath);
 else
  strcpy(szURL, "/");
 //请求HEAD,通过HEAD获得文件大小及类型进行校验 
 hRequestHead = HttpOpenRequestA(hConnect, "HEAD", szURL, "HTTP/1.1", "", NULL, INTERNET_FLAG_RELOAD, 0);
 
 bRet=_TryHttpSendRequest(hRequestHead, 1);//发送5次链接请求
 if (bRet)
 {
  return FALSE;
 }
 //查询content-length大小 
 dwContentLen = 0;
 dwSizeDW = sizeof(DWORD);
 bRet = HttpQueryInfo(hRequestHead, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH, &dwContentLen, &dwSizeDW, NULL);//查询文件大小
 if (!bRet)
 {
  return FALSE;
 }
 //校验完成后再请求GET,下载文件 
 hRequestGet = HttpOpenRequestA(hConnect, "GET", szURL, "HTTP/1.1", "", NULL, INTERNET_FLAG_RELOAD, 0);//打开获取文件句柄
 bRet = _TryHttpSendRequest(hRequestGet, 1);//发送5次链接请求
 if (bRet)
 {
  return FALSE;
 }

 return TRUE;
}
//下载
void THttpGetThread::Download(){
 
 char  *pBuf = new char[DOWNLOAD_BUF_SIZE*sizeof(char)]; //缓冲区 
 dwDownFileTotalBytes = 0;
 while (1)
 {
  dwDownBytes = 0;
  memset(pBuf, 0, DOWNLOAD_BUF_SIZE*sizeof(char));
  bRet = InternetReadFile(hRequestGet, pBuf, DOWNLOAD_BUF_SIZE, &dwDownBytes);//从一个打开的句柄中读取数据true或者false
  //参数,句柄,缓冲区,每次读取最大长度,实际读取的长度。
  if (bRet)
  {
   if (dwDownBytes > 0)
   {
    dwDownFileTotalBytes += dwDownBytes;
    
    bRet = WriteFile(hFileWrite, pBuf, dwDownBytes, &dwWriteBytes, NULL); //写入文件 
    if (bRet)
    {
     
    }
   }
   else if (0 == dwDownBytes)
   {
    bRet = TRUE;
    break; //下载成功完成 
   }
  }
 }
}
//释放资源
void THttpGetThread::ReleaseResources()
{
 CloseHandle(hFileWrite);
 InternetCloseHandle(hRequestGet);
 InternetCloseHandle(hRequestHead);
 InternetCloseHandle(hConnect);
 InternetCloseHandle(hInet);

}
//创建文件
BOOL THttpGetThread::CreateFile()
{
 char path[MAX_PATH] = { 0 };//分配目标缓存
 bRet = FileExists(lpszFileName);
 DWORD dBufSize = WideCharToMultiByte(CP_OEMCP, 0, lpszFileName, -1, NULL, 0, NULL, FALSE);//获取转换所需的目标缓存大小 
 int nRet = WideCharToMultiByte(CP_OEMCP, 0, lpszFileName, -1, path, dBufSize, NULL, FALSE);//转换 
 if (bRet)//如果存在则删除
 {
  remove(path);//删除文件
  hFileWrite = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建文件
 }
 else{
  hFileWrite = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建文件
 }
 if (hFileWrite == NULL)
 {
  return false;
 }
 return true;
}
//检测文件是否存在
BOOL THttpGetThread::FileExists(LPCWSTR lpszFileName)//检测文件是否存在
{
 //试图取得文件属性
 DWORD dwAttributes = ::GetFileAttributesW(lpszFileName);
 if (INVALID_FILE_ATTRIBUTES == dwAttributes)
 {
  return FALSE;
 }
  return TRUE; 
}
//执行
void THttpGetThread::execute(){
 if (analysisURL())
 {
  if (Initialization())
  {
   if (CreateFile())
   {
    Download();
    ReleaseResources();
   }
  }
 }
}

BOOL THttpGetThread::_TryHttpSendRequest(LPVOID hRequest, int nMaxTryTimes)
{
 BOOL bRet = FALSE;
 DWORD dwStatusCode = 0;
 DWORD dwSizeDW = sizeof(DWORD);
 while (hRequest && (nMaxTryTimes-- > 0)) //多次尝试发送请求 
 {
  //发送请求 
  bRet = HttpSendRequestA(hRequest, NULL, 0, NULL, 0);
  if (!bRet)
  {
   continue;
  }
  else
  {
   //判断HTTP返回的状态码 
   dwStatusCode = 0;
   dwSizeDW = sizeof(DWORD);
   bRet = HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, &dwStatusCode, &dwSizeDW, NULL);//用于查询一个http请求
   if (bRet)
   {
    //检查状态码 
    if (HTTP_STATUS_OK == dwStatusCode) //200-OK   401-未授权   404-找不到该资源   500-服务器错误   503– 服务不可用  100(继续) 101(切换协议)
    {

     break;
    }
    else
    {
     bRet = FALSE;
     continue;
    }
   }
  }
 }

 return bRet;
}

最后,我们来看看主函数调用

// 断点续传,多线程.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "THttpGetThread.h"

int _tmain(int argc, _TCHAR* argv[])
{
 THttpGetThread a("http://down.360safe.com/360/inst.exe","E:\\360.exe");//调用构造的时候把,下载地址和存储路径传进去
 a.execute();
 return 0;
}

我的QQ:609162385,写的不好,多交流交流








你可能感兴趣的:(c++)