C/C++通过HTTP实现文件上传与下载的示例详解

  目录

  引言

  WinInet(Windows Internet)是 Microsoft Windows 操作系统中的一个 API 集,用于提供对 Internet 相关功能的支持。它包括了一系列的函数,使得 Windows 应用程序能够进行网络通信、处理 HTTP 请求、FTP 操作等。WinInet 提供了一套完整的网络通信工具,使得开发者能够轻松地构建支持网络功能的应用程序,涵盖了从简单的 HTTP 请求到复杂的文件传输等多种网络操作。

  分解URL地址

  函数可实现对URL字符串进行解析,提取其中的协议、主机名、端口、路径和其他信息,并将这些信息存储在 结构中,方便后续的网络操作,该函数是Windows下默认提供的,函数与依赖结果如下所示;

  函数原型

  BOOL InternetCrackUrl(

  LPCTSTR lpszUrl,

  DWORD dwUrlLength,

  DWORD dwFlags,

  LPURL_COMPONENTS lpUrlComponents

  );

  参数说明

  URL_COMPONENTS结构

  typedef struct {

  DWORD dwStructSize;

  LPTSTR lpszScheme;

  DWORD dwSchemeLength;

  INTERNET_SCHEME nScheme;

  LPTSTR lpszHostName;

  DWORD dwHostNameLength;

  INTERNET_PORT nPort;

  LPTSTR lpszUserName;

  DWORD dwUserNameLength;

  LPTSTR lpszPassword;

  DWORD dwPasswordLength;

  LPTSTR lpszUrlPath;

  DWORD dwUrlPathLength;

  LPTSTR lpszExtraInfo;

  DWORD dwExtraInfoLength;

  } URL_COMPONENTS, *LPURL_COMPONENTS;

  返回值

  如果函数成功,返回 ,并在 结构中存储解析后的信息;如果失败,返回 。在失败时,可以调用 函数获取详细的错误信息。

  函数调用

  #include

  #include

  #include

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

  using namespace std;

  BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, DWORD dwBufferSize)

  {

  BOOL bRet = FALSE;

  URL_COMPONENTS uc = { 0 };

  // 初始化变量中的内容

  RtlZeroMemory(&uc, sizeof(uc));

  RtlZeroMemory(pszScheme, dwBufferSize);

  RtlZeroMemory(pszHostName, dwBufferSize);

  RtlZeroMemory(pszUserName, dwBufferSize);

  RtlZeroMemory(pszPassword, dwBufferSize);

  RtlZeroMemory(pszUrlPath, dwBufferSize);

  RtlZeroMemory(pszExtraInfo, dwBufferSize);

  // 将长度填充到结构中

  uc.dwStructSize = sizeof(uc);

  uc.dwSchemeLength = dwBufferSize - 1;

  uc.dwHostNameLength = dwBufferSize - 1;

  uc.dwUserNameLength = dwBufferSize - 1;

  uc.dwPasswordLength = dwBufferSize - 1;

  uc.dwUrlPathLength = dwBufferSize - 1;

  uc.dwExtraInfoLength = dwBufferSize - 1;

  uc.lpszScheme = pszScheme;

  uc.lpszHostName = pszHostName;

  uc.lpszUserName = pszUserName;

  uc.lpszPassword = pszPassword;

  uc.lpszUrlPath = pszUrlPath;

  uc.lpszExtraInfo = pszExtraInfo;

  // 分解URL地址

  bRet = InternetCrackUrl(pszUrl, 0, 0, &uc);

  if (FALSE == bRet)

  {

  return bRet;

  }

  return bRet;

  }

  int main(int argc, char* argv[])

  {

  char szHttpDownloadUrl[] = "http://www.lyshark.com/index.php&username=lyshark&password=123";

  // 对应的变量

  char szScheme[MAX_PATH] = { 0 };

  char szHostName[MAX_PATH] = { 0 };

  char szUserName[MAX_PATH] = { 0 };

  char szPassword[MAX_PATH] = { 0 };

  char szUrlPath[MAX_PATH] = { 0 };

  char szExtraInfo[MAX_PATH] = { 0 };

  // 初始化用0填充

  RtlZeroMemory(szScheme, MAX_PATH);

  RtlZeroMemory(szHostName, MAX_PATH);

  RtlZeroMemory(szUserName, MAX_PATH);

  RtlZeroMemory(szPassword, MAX_PATH);

  RtlZeroMemory(szUrlPath, MAX_PATH);

  RtlZeroMemory(szExtraInfo, MAX_PATH);

  // 分解URL

  if (FALSE == UrlCrack(szHttpDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))

  {

  return FALSE;

  }

  std::cout << szScheme << std::endl;

  std::cout << szHostName << std::endl;

  std::cout << szUserName << std::endl;

  std::cout << szPassword << std::endl;

  std::cout << szUrlPath << std::endl;

  std::cout << szExtraInfo << std::endl;

  system("pause");

  return 0;

  }

  运行代码输出特定网址的每个部分,如下图所示;

  下载页面内容

  InternetOpen

  用于初始化 WinINet 函数的使用。以下是该函数的原型:

  HINTERNET InternetOpen(

  LPCWSTR lpszAgent,

  DWORD dwAccessType,

  LPCWSTR lpszProxyName,

  LPCWSTR lpszProxyBypass,

  DWORD dwFlags

  );

  参数说明:

  返回值:

  如果函数调用成功,将返回一个类型为 的句柄,用于后续的 WinINet 操作。如果函数调用失败,返回 。可以使用 函数获取详细的错误信息。

  InternetConnect

  用于建立到远程服务器的连接。以下是该函数的原型:

  HINTERNET InternetConnect(

  HINTERNET hInternet,

  LPCWSTR lpszServerName,

  INTERNET_PORT nServerPort,

  LPCWSTR lpszUserName,

  LPCWSTR lpszPassword,

  DWORD dwService,

  DWORD dwFlags,

  DWORD_PTR dwContext

  );

  参数说明:

  返回值:

  如果函数调用成功,将返回一个类型为 的句柄,表示连接的上下文。如果函数调用失败,返回 。可以使用 函数获取详细的错误信息。

  用于建立连接后,可以使用返回的句柄执行相关的协议操作,如 FTP 或 HTTP 操作。使用完连接后,同样需要使用 函数关闭相应的句柄,以释放资源。

  HttpOpenRequest

  它是在使用 WinINet 库进行 HTTP 操作时的一部分。以下是该函数的原型:

  HINTERNET HttpOpenRequest(

  HINTERNET hConnect,

  LPCWSTR lpszVerb,

  LPCWSTR lpszObjectName,

  LPCWSTR lpszVersion,

  LPCWSTR lpszReferrer,

  LPCWSTR *lplpszAcceptTypes,

  DWORD dwFlags,

  DWORD_PTR dwContext

  );

  参数说明:

  返回值:

  如果函数调用成功,将返回一个类型为 的句柄,表示打开的 HTTP 请求。如果函数调用失败,返回 。可以使用 函数获取详细的错误信息。

  一旦打开了 HTTP 请求,可以使用返回的句柄执行发送请求、接收响应等操作。使用完请求后,同样需要使用 函数关闭相应的句柄,以释放资源。

  HttpSendRequest

  用于发送 HTTP 请求的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:

  BOOL HttpSendRequest(

  HINTERNET hRequest,

  LPCWSTR lpszHeaders,

  DWORD dwHeadersLength,

  LPVOID lpOptional,

  DWORD dwOptionalLength

  );

  参数说明:

  返回值:

  如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 函数获取详细的错误信息。

  用于实际发送 HTTP 请求。在调用此函数之后,可以使用其他 WinINet 函数来读取服务器的响应。同样,使用完请求后,需要使用 函数关闭相应的句柄,以释放资源。

  HttpQueryInfo

  用于检索有关 HTTP 请求或响应的信息的函数,通常在使用 WinINet 库进行 HTTP 操作时调用。以下是该函数的原型:

  BOOL HttpQueryInfo(

  HINTERNET hRequest,

  DWORD dwInfoLevel,

  LPVOID lpBuffer,

  LPDWORD lpdwBufferLength,

  LPDWORD lpdwIndex

  );

  参数说明:

  返回值:

  如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 函数获取详细的错误信息。

  用于获取与 HTTP 请求或响应相关的信息,如状态码、内容类型等。注意,在调用此函数之前,通常需要先调用 发送请求。同样,使用完请求后,需要使用 函数关闭相应的句柄,以释放资源。

  InternetReadFile

  用于从指定的句柄读取数据的函数,通常在使用 WinINet 库进行网络操作时调用。以下是该函数的原型:

  BOOL InternetReadFile(

  HINTERNET hFile,

  LPVOID lpBuffer,

  DWORD dwNumberOfBytesToRead,

  LPDWORD lpdwNumberOfBytesRead

  );

  参数说明:

  返回值:

  如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 函数获取详细的错误信息。

  用于从网络资源中读取数据,如从 HTTP 请求的响应中读取内容。在调用此函数之前,通常需要先调用其他相关的函数,如 、 和 。同样,使用完资源后,需要使用 函数关闭相应的句柄,以释放资源。

  下载页面的完整代码是这样的,如下所示;

  #include

  #include

  #include

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

  using namespace std;

  BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, DWORD dwBufferSize)

  {

  BOOL bRet = FALSE;

  URL_COMPONENTS uc = { 0 };

  // 初始化变量中的内容

  RtlZeroMemory(&uc, sizeof(uc));

  RtlZeroMemory(pszScheme, dwBufferSize);

  RtlZeroMemory(pszHostName, dwBufferSize);

  RtlZeroMemory(pszUserName, dwBufferSize);

  RtlZeroMemory(pszPassword, dwBufferSize);

  RtlZeroMemory(pszUrlPath, dwBufferSize);

  RtlZeroMemory(pszExtraInfo, dwBufferSize);

  // 将长度填充到结构中

  uc.dwStructSize = sizeof(uc);

  uc.dwSchemeLength = dwBufferSize - 1;

  uc.dwHostNameLength = dwBufferSize - 1;

  uc.dwUserNameLength = dwBufferSize - 1;

  uc.dwPasswordLength = dwBufferSize - 1;

  uc.dwUrlPathLength = dwBufferSize - 1;

  uc.dwExtraInfoLength = dwBufferSize - 1;

  uc.lpszScheme = pszScheme;

  uc.lpszHostName = pszHostName;

  uc.lpszUserName = pszUserName;

  uc.lpszPassword = pszPassword;

  uc.lpszUrlPath = pszUrlPath;

  uc.lpszExtraInfo = pszExtraInfo;

  // 分解URL地址

  bRet = InternetCrackUrl(pszUrl, 0, 0, &uc);

  if (FALSE == bRet)

  {

  return bRet;

  }

  return bRet;

  }

  // 从响应信息头信息中获取数据内容长度大小

  BOOL GetContentLength(char* pResponseHeader, DWORD* pdwContentLength)

  {

  int i = 0;

  char szContentLength[MAX_PATH] = { 0 };

  DWORD dwContentLength = 0;

  char szSubStr[] = "Content-Length: ";

  RtlZeroMemory(szContentLength, MAX_PATH);

  // 在传入字符串中查找子串

  char* p = strstr(pResponseHeader, szSubStr);

  if (NULL == p)

  {

  return FALSE;

  }

  p = p + lstrlen(szSubStr);

  // 如果找到了就提取出里面的纯数字

  while (('0' <= *p) && ('9' >= *p))

  {

  szContentLength[i] = *p;

  p++;

  i++;

  }

  // 字符串转数字

  dwContentLength = atoi(szContentLength);

  *pdwContentLength = dwContentLength;

  return TRUE;

  }

  // 数据下载

  BOOL HttpDownload(char* pszDownloadUrl, BYTE** ppDownloadData, DWORD* pdwDownloadDataSize)

  {

  // 定义HTTP子变量

  char szScheme[MAX_PATH] = { 0 };

  char szHostName[MAX_PATH] = { 0 };

  char szUserName[MAX_PATH] = { 0 };

  char szPassword[MAX_PATH] = { 0 };

  char szUrlPath[MAX_PATH] = { 0 };

  char szExtraInfo[MAX_PATH] = { 0 };

  // 填充为空

  RtlZeroMemory(szScheme, MAX_PATH);

  RtlZeroMemory(szHostName, MAX_PATH);

  RtlZeroMemory(szUserName, MAX_PATH);

  RtlZeroMemory(szPassword, MAX_PATH);

  RtlZeroMemory(szUrlPath, MAX_PATH);

  RtlZeroMemory(szExtraInfo, MAX_PATH);

  // 拆解URL地址

  if (FALSE == UrlCrack(pszDownloadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))

  {

  return FALSE;

  }

  // 数据下载

  HINTERNET hInternet = NULL;

  HINTERNET hConnect = NULL;

  HINTERNET hRequest = NULL;

  DWORD dwOpenRequestFlags = 0;

  BOOL bRet = FALSE;

  unsigned char* pResponseHeaderIInfo = NULL;

  DWORD dwResponseHeaderIInfoSize = 2048;

  BYTE* pBuf = NULL;

  DWORD dwBufSize = 64 * 1024;

  BYTE* pDownloadData = NULL;

  DWORD dwDownloadDataSize = 0;

  DWORD dwRet = 0;

  DWORD dwOffset = 0;

  do

  {

  // 建立会话

  hInternet = InternetOpen("WinInetGet/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);

  if (NULL == hInternet)

  {

  break;

  }

  // 建立连接

  hConnect = InternetConnect(hInternet, szHostName, INTERNET_DEFAULT_HTTP_PORT, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);

  if (NULL == hConnect)

  {

  break;

  }

  // 打开并发送HTTP请求

  dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |

  INTERNET_FLAG_KEEP_CONNECTION |

  INTERNET_FLAG_NO_AUTH |

  INTERNET_FLAG_NO_COOKIES |

  INTERNET_FLAG_NO_UI |

  INTERNET_FLAG_RELOAD;

  if (0 < lstrlen(szExtraInfo))

  {

  lstrcat(szUrlPath, szExtraInfo);

  }

  // 以GET模式打开请求

  hRequest = HttpOpenRequest(hConnect, "GET", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);

  if (NULL == hRequest)

  {

  break;

  }

  // 发送请求

  bRet = HttpSendRequest(hRequest, NULL, 0, NULL, 0);

  if (FALSE == bRet)

  {

  break;

  }

  // 接收响应的报文信息头(Get Response Header)

  pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize];

  if (NULL == pResponseHeaderIInfo)

  {

  break;

  }

  RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);

  // 查询HTTP请求头

  bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);

  if (FALSE == bRet)

  {

  break;

  }

  // 从字符串中 "Content-Length: " 网页获取数据长度

  bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwDownloadDataSize);

  // 输出完整响应头

  std::cout << pResponseHeaderIInfo << std::endl;

  if (FALSE == bRet)

  {

  break;

  }

  // 接收报文主体内容(Get Response Body)

  pBuf = new BYTE[dwBufSize];

  if (NULL == pBuf)

  {

  break;

  }

  pDownloadData = new BYTE[dwDownloadDataSize];

  if (NULL == pDownloadData)

  {

  break;

  }

  RtlZeroMemory(pDownloadData, dwDownloadDataSize);

  do

  {

  RtlZeroMemory(pBuf, dwBufSize);

  // 循环读入数据并保存在变量中

  bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);

  if (FALSE == bRet)

  {

  break;

  }

  RtlCopyMemory((pDownloadData + dwOffset), pBuf, dwRet);

  dwOffset = dwOffset + dwRet;

  } while (dwDownloadDataSize > dwOffset);

  // 返回数据

  *ppDownloadData = pDownloadData;

  *pdwDownloadDataSize = dwDownloadDataSize;

  } while (FALSE);

  // 关闭并释放资源

  if (NULL != pBuf)

  {

  delete[]pBuf;

  pBuf = NULL;

  }

  if (NULL != pResponseHeaderIInfo)

  {

  delete[]pResponseHeaderIInfo;

  pResponseHeaderIInfo = NULL;

  }

  if (NULL != hRequest)

  {

  InternetCloseHandle(hRequest);

  hRequest = NULL;

  }

  if (NULL != hConnect)

  {

  InternetCloseHandle(hConnect);

  hConnect = NULL;

  }

  if (NULL != hInternet)

  {

  InternetCloseHandle(hInternet);

  hInternet = NULL;

  }

  return bRet;

  }

  // 创建并保存文件

  BOOL SaveToFile(char* pszFileName, BYTE* pData, DWORD dwDataSize)

  {

  // 创建空文件

  HANDLE hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE,

  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,

  FILE_ATTRIBUTE_ARCHIVE, NULL);

  if (INVALID_HANDLE_VALUE == hFile)

  {

  return FALSE;

  }

  DWORD dwRet = 0;

  // 写出数据到文件

  WriteFile(hFile, pData, dwDataSize, &dwRet, NULL);

  // 关闭句柄保存文件

  CloseHandle(hFile);

  return TRUE;

  }

  使用时调用实现数据下载,下载后的文件会保存在中,此时直接调用将其保存在文件中即可;

  int main(int argc, char* argv[])

  {

  // 设置需要下载的地址

  char szHttpDownloadUrl[] = "http://www.lyshark.com/index.html";

  BYTE* pHttpDownloadData = NULL;

  DWORD dwHttpDownloadDataSize = 0;

  // HTTP下载

  if (TRUE == HttpDownload(szHttpDownloadUrl, &pHttpDownloadData, &dwHttpDownloadDataSize))

  {

  std::cout << "已保存文件,长度: " << dwHttpDownloadDataSize << " 字节"<< std::endl;

  }

  // 将下载数据保存成文件

  SaveToFile((char *)"d://index.html", pHttpDownloadData, dwHttpDownloadDataSize);

  // 释放内存

  delete[]pHttpDownloadData;

  pHttpDownloadData = NULL;

  system("pause");

  return 0;

  }

  运行后则可一输出响应头完整参数以及输出的字节数,如下图所示;

  上传文件内容

  服务端,首先需要实现一个简单的上传接收功能,这里使用flask框架实现,通过执行pip install flask命令安装这个库,安装成功以后手动保存为main.py文件,上传文件是只需要向http://127.0.0.1/upload?file=推送数据即可,代码如下;

  from flask import Flask, request

  app = Flask(__name__)

  @app.route('/upload', methods=['POST'])

  def upload_file():

  file = request.args.get('file')

  if not file:

  return "上传错误,文件名未指定"

  try:

  with open(file, 'wb') as uploaded_file:

  uploaded_file.write(request.get_data())

  if os.path.exists(file):

  return "上传成功"

  else:

  return "上传失败"

  except Exception as e:

  return f"上传失败: {str(e)}"

  if __name__ == '__main__':

  app.run(debug=True, host='localhost', port=80)

  服务端以管理员身份运行文件,此时会启用一个Web服务器用于接收客户端的上传请求,如下图所示;

  接着是客户端的实现,首先介绍如下几个关键API函数;

  HttpSendRequestEx

  用于发送带有附加选项的 HTTP 请求。相对于 ,它提供了更多的灵活性,允许在请求中包含额外的信息,例如头部和数据。

  以下是 的原型:

  BOOL HttpSendRequestEx(

  HINTERNET hRequest,

  LPINTERNET_BUFFERS lpBuffersIn,

  LPINTERNET_BUFFERS lpBuffersOut,

  DWORD dwFlags,

  DWORD_PTR dwContext

  );

  参数说明:

  是一个结构,允许您在 HTTP 请求和响应中指定用于发送和接收数据的缓冲区。

  使用 需要谨慎处理内存,并根据您的需求设置 结构的具体方式。

  InternetWriteFile

  用于将数据写入到由 、、 或 等函数打开的 URL、连接或文件。以下是该函数的原型:

  BOOL InternetWriteFile(

  HINTERNET hFile,

  LPCVOID lpBuffer,

  DWORD dwNumberOfBytesToWrite,

  LPDWORD lpdwNumberOfBytesWritten

  );

  参数说明:

  返回值:

  如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 函数获取详细的错误信息。

  主要用于将数据写入网络资源,如通过 HTTP 或 FTP 协议上传文件。在调用此函数之前,通常需要先调用其他相关的函数,如 、、 等。同样,使用完资源后,需要使用 函数关闭相应的句柄,以释放资源。

  HttpEndRequest

  它通常与 或 配合使用,用于完成 HTTP 请求的发送,并准备接收服务器的响应。

  以下是 函数的原型:

  BOOL HttpEndRequest(

  HINTERNET hRequest,

  LPINTERNET_BUFFERS lpBuffersOut,

  DWORD dwFlags,

  DWORD_PTR dwContext

  );

  参数说明:

  返回值:

  如果函数调用成功,返回非零值;如果函数调用失败,返回零。可以使用 函数获取详细的错误信息。

  的主要作用是完成 HTTP 请求的发送,并在请求完成后准备接收服务器的响应。在调用此函数之后,通常会使用 函数等来读取服务器的响应数据。

  上传文件的完整代码是这样的,如下所示;

  #include

  #include

  #include

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

  using namespace std;

  // 切割路径

  BOOL UrlCrack(char* pszUrl, char* pszScheme, char* pszHostName, char* pszUserName, char* pszPassword, char* pszUrlPath, char* pszExtraInfo, DWORD dwBufferSize)

  {

  BOOL bRet = FALSE;

  URL_COMPONENTS uc = { 0 };

  // 初始化变量中的内容

  RtlZeroMemory(&uc, sizeof(uc));

  RtlZeroMemory(pszScheme, dwBufferSize);

  RtlZeroMemory(pszHostName, dwBufferSize);

  RtlZeroMemory(pszUserName, dwBufferSize);

  RtlZeroMemory(pszPassword, dwBufferSize);

  RtlZeroMemory(pszUrlPath, dwBufferSize);

  RtlZeroMemory(pszExtraInfo, dwBufferSize);

  // 将长度填充到结构中

  uc.dwStructSize = sizeof(uc);

  uc.dwSchemeLength = dwBufferSize - 1;

  uc.dwHostNameLength = dwBufferSize - 1;

  uc.dwUserNameLength = dwBufferSize - 1;

  uc.dwPasswordLength = dwBufferSize - 1;

  uc.dwUrlPathLength = dwBufferSize - 1;

  uc.dwExtraInfoLength = dwBufferSize - 1;

  uc.lpszScheme = pszScheme;

  uc.lpszHostName = pszHostName;

  uc.lpszUserName = pszUserName;

  uc.lpszPassword = pszPassword;

  uc.lpszUrlPath = pszUrlPath;

  uc.lpszExtraInfo = pszExtraInfo;

  // 分解URL地址

  bRet = InternetCrackUrl(pszUrl, 0, 0, &uc);

  if (FALSE == bRet)

  {

  return bRet;

  }

  return bRet;

  }

  // 从响应信息头信息中获取数据内容长度大小

  BOOL GetContentLength(char* pResponseHeader, DWORD* pdwContentLength)

  {

  int i = 0;

  char szContentLength[MAX_PATH] = { 0 };

  DWORD dwContentLength = 0;

  char szSubStr[] = "Content-Length: ";

  RtlZeroMemory(szContentLength, MAX_PATH);

  // 在传入字符串中查找子串

  char* p = strstr(pResponseHeader, szSubStr);

  if (NULL == p)

  {

  return FALSE;

  }

  p = p + lstrlen(szSubStr);

  // 如果找到了就提取出里面的纯数字

  while (('0' <= *p) && ('9' >= *p))

  {

  szContentLength[i] = *p;

  p++;

  i++;

  }

  // 字符串转数字

  dwContentLength = atoi(szContentLength);

  *pdwContentLength = dwContentLength;

  return TRUE;

  }

  // 数据上传

  BOOL HttpUpload(char* pszUploadUrl, BYTE* pUploadData, DWORD dwUploadDataSize)

  {

  // 初始化变量中的内容

  char szScheme[MAX_PATH] = { 0 };

  char szHostName[MAX_PATH] = { 0 };

  char szUserName[MAX_PATH] = { 0 };

  char szPassword[MAX_PATH] = { 0 };

  char szUrlPath[MAX_PATH] = { 0 };

  char szExtraInfo[MAX_PATH] = { 0 };

  // 将长度填充到结构中

  RtlZeroMemory(szScheme, MAX_PATH);

  RtlZeroMemory(szHostName, MAX_PATH);

  RtlZeroMemory(szUserName, MAX_PATH);

  RtlZeroMemory(szPassword, MAX_PATH);

  RtlZeroMemory(szUrlPath, MAX_PATH);

  RtlZeroMemory(szExtraInfo, MAX_PATH);

  // 分解URL地址

  if (FALSE == UrlCrack(pszUploadUrl, szScheme, szHostName, szUserName, szPassword, szUrlPath, szExtraInfo, MAX_PATH))

  {

  return FALSE;

  }

  // 数据上传

  HINTERNET hInternet = NULL;

  HINTERNET hConnect = NULL;

  HINTERNET hRequest = NULL;

  DWORD dwOpenRequestFlags = 0;

  BOOL bRet = FALSE;

  DWORD dwRet = 0;

  unsigned char* pResponseHeaderIInfo = NULL;

  DWORD dwResponseHeaderIInfoSize = 2048;

  BYTE* pBuf = NULL;

  DWORD dwBufSize = 64 * 1024;

  BYTE* pResponseBodyData = NULL;

  DWORD dwResponseBodyDataSize = 0;

  DWORD dwOffset = 0;

  DWORD dwPostDataSize = dwUploadDataSize;

  INTERNET_BUFFERS internetBuffers = { 0 };

  do

  {

  // 建立会话

  hInternet = InternetOpen("WinInetPost/0.1", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);

  if (NULL == hInternet)

  {

  break;

  }

  // 建立连接

  hConnect = InternetConnect(hInternet, szHostName, INTERNET_DEFAULT_HTTP_PORT, szUserName, szPassword, INTERNET_SERVICE_HTTP, 0, 0);

  if (NULL == hConnect)

  {

  break;

  }

  // 打开并发送HTTP请求

  dwOpenRequestFlags = INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |

  INTERNET_FLAG_KEEP_CONNECTION |

  INTERNET_FLAG_NO_AUTH |

  INTERNET_FLAG_NO_COOKIES |

  INTERNET_FLAG_NO_UI |

  INTERNET_FLAG_RELOAD;

  if (0 < lstrlen(szExtraInfo))

  {

  lstrcat(szUrlPath, szExtraInfo);

  }

  // 使用POST请求

  hRequest = HttpOpenRequest(hConnect, "POST", szUrlPath, NULL, NULL, NULL, dwOpenRequestFlags, 0);

  if (NULL == hRequest)

  {

  break;

  }

  // 告诉服务器传输数据的总大小

  RtlZeroMemory(&internetBuffers, sizeof(internetBuffers));

  internetBuffers.dwStructSize = sizeof(internetBuffers);

  internetBuffers.dwBufferTotal = dwPostDataSize;

  bRet = HttpSendRequestEx(hRequest, &internetBuffers, NULL, 0, 0);

  if (FALSE == bRet)

  {

  break;

  }

  // 发送数据

  bRet = InternetWriteFile(hRequest, pUploadData, dwUploadDataSize, &dwRet);

  if (FALSE == bRet)

  {

  break;

  }

  // 发送完毕

  bRet = HttpEndRequest(hRequest, NULL, 0, 0);

  if (FALSE == bRet)

  {

  break;

  }

  // 接收响应报文

  pResponseHeaderIInfo = new unsigned char[dwResponseHeaderIInfoSize];

  if (NULL == pResponseHeaderIInfo)

  {

  break;

  }

  RtlZeroMemory(pResponseHeaderIInfo, dwResponseHeaderIInfoSize);

  bRet = HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, pResponseHeaderIInfo, &dwResponseHeaderIInfoSize, NULL);

  if (FALSE == bRet)

  {

  break;

  }

  // 获取数据长度

  bRet = GetContentLength((char*)pResponseHeaderIInfo, &dwResponseBodyDataSize);

  if (FALSE == bRet)

  {

  break;

  }

  // 输出响应头

  std::cout << pResponseHeaderIInfo << std::endl;

  // 接收报文主体内容(Get Response Body)

  pBuf = new BYTE[dwBufSize];

  if (NULL == pBuf)

  {

  break;

  }

  pResponseBodyData = new BYTE[dwResponseBodyDataSize];

  if (NULL == pResponseBodyData)

  {

  break;

  }

  RtlZeroMemory(pResponseBodyData, dwResponseBodyDataSize);

  do

  {

  // 循环读取数据并填充到缓冲区内

  RtlZeroMemory(pBuf, dwBufSize);

  bRet = InternetReadFile(hRequest, pBuf, dwBufSize, &dwRet);

  if (FALSE == bRet)

  {

  break;

  }

  RtlCopyMemory((pResponseBodyData + dwOffset), pBuf, dwRet);

  dwOffset = dwOffset + dwRet;

  } while (dwResponseBodyDataSize > dwOffset);

  } while (FALSE);

  // 关闭释放

  if (NULL != pResponseBodyData)

  {

  delete[]pResponseBodyData;

  pResponseBodyData = NULL;

  }

  if (NULL != pBuf)

  {

  delete[]pBuf;

  pBuf = NULL;

  }

  if (NULL != pResponseHeaderIInfo)

  {

  delete[]pResponseHeaderIInfo;

  pResponseHeaderIInfo = NULL;

  }

  if (NULL != hRequest)

  {

  InternetCloseHandle(hRequest);

  hRequest = NULL;

  }

  if (NULL != hConnect)

  {

  InternetCloseHandle(hConnect);

  hConnect = NULL;

  }

  if (NULL != hInternet)

  {

  InternetCloseHandle(hInternet);

  hInternet = NULL;

  }

  return bRet;

  }

  上传代码通过指定将文件提交到远程主机,代码如下所示;

  int main(int argc, char* argv[])

  {

  // 设置上传接口地址

  char szHttpUploadUrl[] = "http://127.0.0.1/upload?file=lyshark.exe";

  // 被上传文件绝对路径

  char szHttpUploadFileName[] = "d://lyshark.exe";

  BYTE* pHttpUploadData = NULL;

  DWORD dwHttpUploadDataSize = 0;

  DWORD dwRet = 0;

  // 打开文件

  HANDLE hFile = CreateFile(szHttpUploadFileName, GENERIC_READ | GENERIC_WRITE,

  FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,

  FILE_ATTRIBUTE_ARCHIVE, NULL);

  // 获取文件大小

  dwHttpUploadDataSize = GetFileSize(hFile, NULL);

  // 读取文件数据

  pHttpUploadData = new BYTE[dwHttpUploadDataSize];

  ReadFile(hFile, pHttpUploadData, dwHttpUploadDataSize, &dwRet, NULL);

  dwHttpUploadDataSize = dwRet;

  // 上传数据

  if (FALSE == HttpUpload(szHttpUploadUrl, pHttpUploadData, dwHttpUploadDataSize))

  {

  return 0;

  }

  // 释放内存

  delete[]pHttpUploadData;

  pHttpUploadData = NULL;

  CloseHandle(hFile);

  system("pause");

  return 0;

  }

  运行后提交文件,则可以看到输出信息,如下图所示;

  以上就是C/C++通过HTTP实现文件上传与下载的示例详解的详细内容,更多关于C/C++ HTTP实现文件上传下载的资料请关注脚本之家其它相关文章!

  您可能感兴趣的文章: