一、前言
cURL是一个利用URL语法在命令行下工作的文件传输工具,1997年首次发行。它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下载工具。cURL还包含了用于程序开发的libcurl。
cURL支持的通信协议有FTP、FTPS、HTTP、HTTPS、TFTP、SFTP、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3、SMTP和RTSP。
curl还支持SSL认证、HTTP POST、HTTP PUT、FTP上传, HTTP form based upload、proxies、HTTP/2、cookies、用户名+密码认证(Basic, Plain, Digest, CRAM-MD5, NTLM, Negotiate and Kerberos)、file transfer resume、proxy tunneling。
二、curl下载
curl for windows : https://curl.se/windows/
下载页面如图: windows下C语言使用curl库访问HTTP下载文件_文件下载](https://file.cfanz.cn/uploads/png/2022/05/21/3/60b25N4O9S.png)
 windows下C语言使用curl库访问HTTP下载文件_文件下载_02](https://file.cfanz.cn/uploads/png/2022/05/21/3/23adJ9d0ba.png) 解压后的可执行文件位置:
解压后的可执行文件位置: windows下C语言使用curl库访问HTTP下载文件_文件下载_03](https://file.cfanz.cn/uploads/png/2022/05/21/3/Fb3ded6HM1.png) 下面是解压后的文件目录:
下面是解压后的文件目录: windows下C语言使用curl库访问HTTP下载文件_文件下载_04](https://file.cfanz.cn/uploads/png/2022/05/21/3/3O8K5JIF22.png) 在命令行使用curl测试下载文件:
在命令行使用curl测试下载文件: windows下C语言使用curl库访问HTTP下载文件_文件下载_05](https://file.cfanz.cn/uploads/png/2022/05/21/3/c92WI9M6Y1.png)
三、通过命令行使用curl
curl可以直接调用函数库完成功能设计、也可以直接调用可执行文件完成需要的功能,下面这里就介绍,在windows下,通过CreateProcess调用curl命令函数完成文件下载。使用curl实现HTTP协议文件下载成功,通过给定的连接地址,可以完成文件下载,百分比进度返回等等。
/**************************************************
作者: DS小龙哥
功能: 执行命令
参数解释:
CallBackFunction_p func_p  :回调函数,用于通知进度执行过程
char *text  //进度的转码过程,详细描述.描述当前这个操作是做什么.
char *total_time   //执行的总时间
char *cmd //执行的命令
**************************************************/
int file_down_func(CallBackFunction_p func_p, const char *text, const char *total_time, const  char *cmd)
{
    BOOL run_pipe;
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    BOOL ret = FALSE;
    DWORD flags = CREATE_NO_WINDOW;
    char pBuffer[210];
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    HANDLE hReadPipe, hWritePipe;
    run_pipe = CreatePipe(&hReadPipe, &hWritePipe, &sa, 0);
    if (run_pipe != 1)
    {
      printf("创建匿名管道文件失败=%d\n", run_pipe);
      return -1;
    }
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags |= STARTF_USESTDHANDLES;
    si.hStdInput = NULL;
    si.hStdError = hWritePipe;
    si.hStdOutput = hWritePipe;
    wchar_t cmd_wchar[1024];
    CharToWchar(cmd, cmd_wchar);
    //TCHAR cmd[] = TEXT("ffmpeg -i D:\\123.mp4 -vf reverse D:\\out\\out1.mp4");
    ret = CreateProcess(NULL, cmd_wchar, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
    if (ret)
    {
      while (true)
      {
        DWORD ExitCode = 0;
        //判断进程是否执行结束
        printf("正在执行...GetExitCodeProcess\r\n");
        GetExitCodeProcess(pi.hProcess, &ExitCode);
        printf("ExitCode:%d\r\n", ExitCode);
        if (ExitCode == STILL_ACTIVE) //正在运行
        {
          DWORD RSize = 0;
          BOOL run_s = 0;
          printf("正在执行...ReadFile\r\n");
          run_s = ReadFile(hReadPipe, pBuffer, sizeof(pBuffer), &RSize, NULL);
          pBuffer[RSize - 1] = '\0';
          printf("执行过程:%s,%d,%d,%s\n", version_str, run_s, RSize, pBuffer);
          char number_buff[10]="\0"; //存放百分比
          printf("pBuffer=%s\r\n", pBuffer);
          //通过回调函数向外部返回进度提示
          for (size_t i = 0; i < 10 && pBuffer[i]!='\0'; i++)
          {
            if (pBuffer[i] >= '0' && pBuffer[i] <= '9')
            {
              //得到百分比值
              for (size_t j = 0; j < 9 && pBuffer[i+j] != '\0'; j++)
              {
                //printf("@@%c@@\r\n", pBuffer[i + j]);
                if (pBuffer[i+j] >= '0' && pBuffer[i+j] <= '9')
                {
                  number_buff[j] = pBuffer[i + j];
                }
                else
                {
                  number_buff[j] = '\0';
                  break;
                }
              }
              break;
            }
          }
          //  0  926M    0 6463k    0     0  7378k      0  0:02:08 --:--:--  0:02:08 7386
          //如果找到进度的位置
          if (strlen(number_buff)>0)
          {
            std::string  out_str;
            out_str = text;
            out_str += ",";
            out_str += "100";
            out_str += ",";
            out_str += number_buff;
            printf("回调:%s\r\n", out_str.c_str());
            //将执行的结果再回调出去
            if (func_p)
            {
              func_p(out_str.c_str());
            }
          }
        }
        else //结束
        {
          printf("执行完毕,ExitCode=%d\r\n", ExitCode);
          break;
        }
      }
      printf("正在等待子进程结束....\n");
      //等待结束
      WaitForSingleObject(pi.hProcess, INFINITE);
      CloseHandle(pi.hProcess);
      CloseHandle(pi.hThread);
      printf("子进程执行完毕....\n");
      return 0;
    }
    printf("子进程创建失败:%d\n", ret);
  return -1;
}
int main()
{
      string VideoCacheFilePath = "D:\\out";
  //下载的文件名称
  string file_path = "http://192.168.1.110:8001/Uploads/1/哈哈哈.MP4";
  //如果返回为真就表示是网络地址
  if (strstr(file_path.c_str(), "http:") ||
    strstr(file_path.c_str(), "https:"))
  {
    //1.获取不带路径的文件名
    string::size_type iPos;
    if (strstr(file_path.c_str(), "\\"))
    {
      iPos = file_path.find_last_of('\\') + 1;
    }
    else
    {
      iPos = file_path.find_last_of('/') + 1;
    }
    //得到文件名称
    string base_file = file_path.substr(iPos, file_path.length() - iPos);
    //得到完整的文件下载存储路径
    string VideoPath_tmp = VideoCacheFilePath;
    VideoPath_tmp += "\\";
    VideoPath_tmp += base_file;
    //判断文件是否存在
    printf("文件存储路径:%s\r\n", VideoPath_tmp.c_str());
    FILE *file_p = fopen(VideoPath_tmp.c_str(),"rb");
    //不存在就下载
    if (file_p == nullptr)
    {
      //切换目录,进入到curl命令所在的目录
      _chdir(VideoCacheFilePath.c_str());
      string cmd = "curl -O ";
      cmd += file_path;
      //启动下载文件
      file_down_func(NULL, base_file.c_str(),"100",cmd.c_str());
    }
    else
    {
      printf("文件存在不需要下载.\r\n");
      fclose(file_p);
    }
  }
  return 0;
}









