使用WinINet实现HTTP下载

news/2024/7/11 5:23:57 标签: internet, download, null, query, buffer, url

网上很难找到比较好的使用WinINet实现HTTP下载的代码,经过半天的研究MSDN(可以搜索关键字:MSDN HTTP sessions,链接:http://msdn.microsoft.com/en-us/library/aa384322(v=vs.85).aspx),还有在google的code搜索中找了一些例子(比如:这里),终于自己实现了一个简单的下载函数,记录到这里备忘。

函数特点:通过先发送一次HEAD请求,获得要下载资源的Header,可以对待下载的资源的类型和大小进行限制。另外,请求失败时自动尝试重新发送几次请求。

 

TODO: 如果要实现断点续传,可以在发送请求时,使用HttpAddRequestHeaders()添加Range头域,请求服务器文件的某个部分。(参见HTTP协议)

 

HTTP协议Range头域介绍:

  Range头域
  Range头域可以请求实体的一个或者多个子范围。例如,
  表示头500个字节:bytes=0-499
  表示第二个500字节:bytes=500-999
  表示最后500个字节:bytes=-500
  表示500字节以后的范围:bytes=500-
  第一个和最后一个字节:bytes=0-0,-1
  同时指定几个范围:bytes=500-600,601-999
  但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200(OK)。

 

 

//code by MulinB, 2011-07-27

#include <windows.h>
#include <wininet.h>

#include <string>
#include <iostream>
using namespace std;

#pragma comment(lib, "wininet.lib")

//下载
#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


BOOL _TryHttpSendRequest(LPVOID hRequest, int nMaxTryTimes); //多次发送请求函数

//HTTP下载函数,通过先请求HEAD的方式然后GET,可以通过HEAD对下载的文件类型和大小做限制
BOOL DownloadUrl(std::string strUrl, std::string strFileName)
{
    BOOL bRet = FALSE;
    if (strUrl == "" || strFileName == "")
        return FALSE;

    //定义变量
    HINTERNET hInet = NULL; //打开internet连接handle
    HINTERNET hConnect = NULL; //HTTP连接
    HINTERNET hRequestHead = NULL; //HTTP Request
    HINTERNET hRequestGet = NULL; //HTTP Request
    HANDLE hFileWrite = NULL; //写文件的句柄
    char* pBuf = NULL; //缓冲区
    DWORD dwRequestTryTimes = MAX_DOWNLOAD_REQUEST_TIME; //尝试请求的次数
    DWORD dwDownBytes = 0; //每次下载的大小
    DWORD dwDownFileTotalBytes = 0; //下载的文件总大小
    DWORD dwWriteBytes = 0; //写入文件的大小
    char bufQueryInfo[LEN_OF_BUFFER_FOR_QUERYINFO] = {0}; //用来查询信息的buffer
    DWORD dwBufQueryInfoSize = sizeof(bufQueryInfo);
    DWORD dwStatusCode = 0;
    DWORD dwContentLen = 0;
    DWORD dwSizeDW = sizeof(DWORD);

    //分割URL
    CHAR pszHostName[INTERNET_MAX_HOST_NAME_LENGTH] = {0};
    CHAR pszUserName[INTERNET_MAX_USER_NAME_LENGTH] = {0};
    CHAR pszPassword[INTERNET_MAX_PASSWORD_LENGTH] = {0};
    CHAR pszURLPath[INTERNET_MAX_URL_LENGTH] = {0};
    CHAR szURL[INTERNET_MAX_URL_LENGTH] = {0};
    URL_COMPONENTSA urlComponents = {0};
    urlComponents.dwStructSize = sizeof(URL_COMPONENTSA);
    urlComponents.lpszHostName = pszHostName;
    urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
    urlComponents.lpszUserName = pszUserName;
    urlComponents.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
    urlComponents.lpszPassword = pszPassword;
    urlComponents.dwPasswordLength = INTERNET_MAX_PASSWORD_LENGTH;
    urlComponents.lpszUrlPath = pszURLPath;
    urlComponents.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;

    bRet = InternetCrackUrlA(strUrl.c_str(), 0, NULL, &urlComponents);
    bRet = (bRet && urlComponents.nScheme == INTERNET_SERVICE_HTTP);
    if (!bRet)
    {
        goto _END_OF_DOWNLOADURL;
    }
    
    //打开一个internet连接
    hInet = InternetOpenA(DOWNHELPER_AGENTNAME, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL);
    if (!hInet)
    {
        bRet = FALSE;
        goto _END_OF_DOWNLOADURL;
    }
    
    //打开HTTP连接
    hConnect = InternetConnectA(hInet, pszHostName, urlComponents.nPort, pszUserName, pszPassword, INTERNET_SERVICE_HTTP, 0, NULL);
    if (!hConnect)
    {
        bRet = FALSE;
        goto _END_OF_DOWNLOADURL;
    }
    
    //创建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, dwRequestTryTimes);
    if (!bRet)
    {
        goto _END_OF_DOWNLOADURL; //请求HEAD失败
    }
   
    //查询content-length大小
    dwContentLen = 0;
    dwSizeDW = sizeof(DWORD);
    bRet = HttpQueryInfo(hRequestHead, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH, &dwContentLen, &dwSizeDW, NULL);
    if (bRet)
    {
        //检查是否文件过大
        if (dwContentLen > MAX_DOWNLOAD_BYTESIZE)
        {
            bRet = FALSE;
            goto _END_OF_DOWNLOADURL;
        }
    }

    //校验完成后再请求GET,下载文件
    hRequestGet = HttpOpenRequestA(hConnect, "GET", szURL, "HTTP/1.1", "", NULL, INTERNET_FLAG_RELOAD, 0);
    bRet = _TryHttpSendRequest(hRequestGet, dwRequestTryTimes);
    if (!bRet)
    {
        goto _END_OF_DOWNLOADURL; //请求HEAD失败
    }

    //创建文件
    hFileWrite = CreateFileA(strFileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hFileWrite)
    {
        bRet = FALSE;
        goto _END_OF_DOWNLOADURL;
    }

    //分配缓冲
    pBuf = new char[DOWNLOAD_BUF_SIZE]; //分配内存
    if (!pBuf)
    {
        bRet = FALSE;
        goto _END_OF_DOWNLOADURL;
    }

    //多次尝试下载文件
    dwDownFileTotalBytes = 0;
    while (1)
    {
        dwDownBytes = 0;
        memset(pBuf, 0, DOWNLOAD_BUF_SIZE*sizeof(char));
        bRet = InternetReadFile(hRequestGet, pBuf, DOWNLOAD_BUF_SIZE, &dwDownBytes);
        if (bRet)
        {
            if (dwDownBytes > 0)
            {
                dwDownFileTotalBytes += dwDownBytes;
                bRet = WriteFile(hFileWrite, pBuf, dwDownBytes, &dwWriteBytes, NULL); //写入文件
                if (!bRet)
                {
                    goto _END_OF_DOWNLOADURL;
                }
            }
            else if (0 == dwDownBytes)
            {
                bRet = TRUE;
                break; //下载成功完成
            }
        }
    }
    
    //清理
_END_OF_DOWNLOADURL:
    if (INVALID_HANDLE_VALUE != hFileWrite)
        CloseHandle(hFileWrite);
    if (pBuf)
        delete [] pBuf;
    if (hRequestGet)
        InternetCloseHandle(hRequestGet);
    if (hRequestHead)
        InternetCloseHandle(hRequestHead);
    if (hConnect)
        InternetCloseHandle(hConnect);
    if (hInet)
        InternetCloseHandle(hInet);
    
    return bRet;
}

//多次发送请求函数
BOOL _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_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, &dwStatusCode, &dwSizeDW, NULL);
            if (bRet)
            {
                //检查状态码
                if (HTTP_STATUS_OK == dwStatusCode) //200 OK
                {
                    break;
                }
                else
                {
                    bRet = FALSE;
                    continue;
                }
            }
        }
    }

    return bRet;
}


int main(int argc, char* argv[])
{
    cout << "正在下载...";
	BOOL bR = DownloadUrl("http://42.duote.com.cn/office2007.zip", "test.zip");
    if (bR)
        cout << "完成" << endl;
    else
        cout << "失败" << endl;
    return 0;
}


 


http://www.niftyadmin.cn/n/1866031.html

相关文章

动态规划算法和贪婪算法的区别

如果看教科书&#xff0c;经常会看到&#xff0c;动态规划算法适用条件有三个&#xff1a; 1.最优化原理&#xff08;最优子结构性质&#xff09; 最优化原理可这样阐述&#xff1a;一个最优化策略具有这样的性质&#xff0c;不论过去状态和决策如何&#xff0c;对前面的决策…

高斯滤波/高斯平滑/高斯模糊的实现及其快速算法(Gaussian Filter, Gaussian Smooth, Gaussian Blur, Fast implementation)

网上介绍针对图像进行高斯模糊的文章不少&#xff0c;其原理比较简单&#xff0c;这里就不做过多介绍。这里简单总结一下实现高斯模糊的几种算法(假设图像大小是M*N&#xff0c;filter的半径是r&#xff0c;注意&#xff0c;很多文章使用的术语是filter size&#xff0c;指的是…

图片配置文件设置 索尼a7s2_索尼微单a7R4、a7R3 a7R2系列相机专题课程

作为号称市场占有率已经第二的索尼索尼微单用户应该已经不少了但是很多入手了索尼微单的朋友都知道索尼的相机功能键有些反人类设计很不习惯为了不让您的相机吃灰或者一直在使用错误的拍摄方式本次系统课程就从超级实用的索尼微单相机设置讲起索尼菜单逻辑混乱&#xff1f;是的…

图像处理中的全局优化技术(Global optimization techniques in image processing and computer vision) (一)

MulinB按&#xff1a;最近打算好好学习一下几种图像处理和计算机视觉中常用的 global optimization (或 energy minimization) 方法&#xff0c;这里总结一下学习心得。分为以下几篇&#xff1a; 1. Discrete Optimization: Graph Cuts and Belief Propagation (本篇) 2. Qu…

vant 怎么显示评分 评分组件_张一山版本的鹿鼎记评分垫底,这些童星怎么回事,个个失去光环...

张一山版本的鹿鼎记评分只有2.6分&#xff0c;这真是让很多观众觉得想不到。张一山是童星出生&#xff0c;当年所为大家带来的家有儿女真的成为搞笑担当&#xff0c;在长大之后余罪中的表现也非常出色&#xff0c;被人们称之为实力派的年轻演员&#xff0c;可是现在到底怎么了&…

图像处理中的全局优化技术(Global optimization techniques in image processing and computer vision) (二)

MulinB按&#xff1a;最近打算好好学习一下几种图像处理和计算机视觉中常用的 global optimization (或 energy minimization) 方法&#xff0c;这里总结一下学习心得。分为以下几篇&#xff1a; 1. Discrete Optimization: Graph Cuts and Belief Propagation 2. Quadratic…

electron 如何拦截关闭事件_Electron+React+Antd(2)工程搭建

electron桌面应用构建指南开发环境 and 正式环境 预览命令配置安装依赖yarn add cross-env --devyarn add concurrently --devyarn add wait-on --dev修改 public/electron.js 新增环境判断// electron 主进程 const { app, BrowserWindow } require(electron);const isPro p…

图像处理中的全局优化技术(Global optimization techniques in image processing and computer vision) (三)

MulinB按&#xff1a;最近打算好好学习一下几种图像处理和计算机视觉中常用的 global optimization (或 energy minimization) 方法&#xff0c;这里总结一下学习心得。分为以下几篇&#xff1a; 1. Discrete Optimization: Graph Cuts and Belief Propagation 2. Quadratic…