您的位置:首页 > 编程语言 > C语言/C++

c++实现下载文件

2016-09-19 11:08 225 查看

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

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

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

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

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

由于,我是封装好的类,

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

#pragma once

#include <windows.h> 

#include <wininet.h>

#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 <stdio.h>

#include<string.h>

#include <windows.h>

#include <wininet.h>

#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,写的不好,多交流交流
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: