Quellcode durchsuchen

V0.6.1
1、修改了部分CurlFtp的操作

Apple vor 3 Monaten
Ursprung
Commit
a7c6beeac6
3 geänderte Dateien mit 378 neuen und 177 gelöschten Zeilen
  1. 353 173
      common/CurlFtp/CurlFtp.cpp
  2. 14 4
      common/CurlFtp/CurlFtp.h
  3. 11 0
      common/CurlFtp/CurlFtpInfo.h

+ 353 - 173
common/CurlFtp/CurlFtp.cpp

@@ -86,12 +86,28 @@ static int writeStringListCallback(void* buffer, size_t size, size_t nmemb, void
  * @param pFile 文件指针
  * @return size_t 实际读取的大小
  */
-static size_t writeDataCallBack(void* contents, size_t size, size_t nmemb, std::ostream* pFile)
+static size_t writeFileCallBack(void* contents, size_t size, size_t nmemb, std::ostream* pFile)
 {
     pFile->write(reinterpret_cast<char*>(contents), size * nmemb);
     return size * nmemb;
 }
 
+/**
+ * @brief 写入数据到vector中
+ * 
+ * @param contents 
+ * @param size 
+ * @param nmemb 
+ * @param vecData 
+ * @return size_t 
+ */
+static size_t writeDataCallBack(void* contents, size_t size, size_t nmemb, std::vector<char>* vecData)
+{
+    size_t copySize = size * nmemb;
+    vecData->insert(vecData->end(), (char*)contents, (char*)contents + copySize);
+    return copySize;
+}
+
 /**
  * @brief 读取文件回调函数
  * 
@@ -101,7 +117,7 @@ static size_t writeDataCallBack(void* contents, size_t size, size_t nmemb, std::
  * @param pFile 文件指针
  * @return size_t 写入的大小
  */
-static size_t readDataCallBack(void* contents, size_t size, size_t nmemb, std::istream* pFile)
+static size_t readFileCallBack(void* contents, size_t size, size_t nmemb, std::istream* pFile)
 {    
     pFile->read(reinterpret_cast<char*>(contents), size * nmemb);
     /* 获取读取到的字节数,可能读取到文件末尾,所以不能直接使用传入的字节数 */
@@ -111,6 +127,33 @@ static size_t readDataCallBack(void* contents, size_t size, size_t nmemb, std::i
     
 }
 
+/**
+ * @brief 
+ * 
+ * @param contents ftp需要的目标内容
+ * @param size 拷贝的数据大小
+ * @param nmemb 单个数据的字节数
+ * @param pData 源指针
+ * @return size_t 已拷贝的数据大小
+ */
+static size_t readDataCallBack(void* contents, size_t size, size_t nmemb, CF_ArrayInfo* pData)
+{
+    if(pData == nullptr)
+    {
+        return 0;
+    }
+    /* 判断是否还够本次拷贝的字节数 */
+    size_t copySize = size * nmemb;
+    if(pData->size - pData->pos < copySize)
+    {
+        copySize = pData->size - pData->pos;
+    }
+    memcpy(contents, pData->data + pData->pos, copySize);
+    pData->pos += copySize;
+
+    return copySize;
+}
+
 /**
  * @brief 计算速度
  * 
@@ -378,150 +421,15 @@ CurlFtp::~CurlFtp()
 }
 
 
-/**
- * @brief 列出FTP文件夹
- * 
- * @param ftpUrl 需要列出文件夹的FTP地址
- * @param username 
- * @param password 
- * @return std::string 
- */
-// std::string CurlFtp::listDir(const std::string &ftpUrl, const std::string &username, const std::string &password)
-// {
-//     CURL *curl;
-//     CURLcode res;
-//     bool result = false;
-//     std::string retList;
-//     /* 1. 初始化curl,这个函数需要在调用任何curl函数之前 */
-//     curl_global_init(CURL_GLOBAL_DEFAULT);
-//     /* 2. 获取一个curl句柄 */
-//     curl = curl_easy_init();
-//     if(curl) 
-//     {
-//         /* 3. 设置curl选项 */
-//         curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.c_str());
-//         curl_easy_setopt(curl, CURLOPT_USERNAME, username.c_str());
-//         curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str());
-//         // curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L); // We only want the directory listing
-
-//         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback);
-//         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &retList);
-
-//         /* 4. 发送信息,一直等待知道返回 */
-//         res = curl_easy_perform(curl);
-//         // printf("res = %d\n", res);
-//         if(res != CURLE_OK) 
-//         {
-//             fprintf(stderr, "getDirList() failed, error code %d, :%s\n",res, curl_easy_strerror(res));
-//         } else 
-//         {
-//             // std::cout << "Directory list: \n" << retList << std::endl;
-//         }
-//         /* 5. 清理curl */
-//         curl_easy_cleanup(curl);
-//     }
-//     if(hasInstace == false)
-//     {
-//         curl_global_cleanup();
-//     }
-//     return retList;
-// }
-
-
-
-
-
-/* 列出文件夹中的所有文件 */
-// bool CurlFtp::listFiles(const std::string &ftpUrl, const std::string &username, const std::string &password, std::vector<std::string>& fileList)
-// {
-//     CURL *curl;
-//     CURLcode res;
-//     bool result = false;
-
-//     curl_global_init(CURL_GLOBAL_DEFAULT);
-//     curl = curl_easy_init();
-//     if(curl)
-//     {
-//         curl_easy_setopt(curl, CURLOPT_URL, (ftpUrl).c_str());
-//         curl_easy_setopt(curl, CURLOPT_USERNAME, username.c_str());
-//         curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str());
-//         curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L);
-//         curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringListCallback);
-//         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fileList);
-
-//         res = curl_easy_perform(curl);
-//         if(res != CURLE_OK) 
-//         {
-//             fprintf(stderr, "Failed to get file list, error code :%d ,%s\n", res, curl_easy_strerror(res));
-//         }
-//         else
-//         {
-//             // for(const std::string& filename : fileList)
-//             // {
-//             //     printf("%s\n", filename.c_str());
-//             // }
-//             result = true;
-//         }
-
-//         curl_easy_cleanup(curl);
-//     }
-
-//     if(hasInstace == false)
-//     {
-//         curl_global_cleanup();
-//     }
-//     return result;
-// }
-
-
-
-/* 创建文件夹 */
-// bool CurlFtp::createDir(const std::string &ftpUrl, const std::string &username, const std::string &password, const std::string &dirName)
-// {
-//     CURL *curl;
-//     CURLcode res;
-//     bool result = false;
-
-//     curl_global_init(CURL_GLOBAL_DEFAULT);
-//     curl = curl_easy_init();
-//     if(curl)
-//     {
-//         curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.c_str());
-//         curl_easy_setopt(curl, CURLOPT_USERNAME, username.c_str());
-//         curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str());
-
-//         // Create a list of FTP commands to be executed on the server
-//         struct curl_slist *headerlist = NULL;
-//         std::string mkdir = "MKD " + dirName;
-//         headerlist = curl_slist_append(headerlist, mkdir.c_str());
-
-//         // Set the list of FTP commands to be executed on the server before the transfer
-//         curl_easy_setopt(curl, CURLOPT_QUOTE, headerlist);
-//         // Tell libcurl to not include the body in the output
-//         curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
-
-//         res = curl_easy_perform(curl);
-
-//         if(res != CURLE_OK) 
-//         {
-//             fprintf(stderr, "createDir() failed, error code :%d ,%s\n", res, curl_easy_strerror(res));
-//             result = false;
-//         }else
-//         {
-//             result = true;
-//         }
-
-//         curl_easy_cleanup(curl);
-//         curl_slist_free_all(headerlist);
-//     }
-
-//     if(hasInstace == false)
-//     {
-//         curl_global_cleanup();
-//     }
-//     return result;
-// }
 
+/* 设置用户名和密码 */
+bool CurlFtp::setUsernameAndPassword(const std::string& username, const std::string& password)
+{
+    m_username = username;
+    m_password = password;
+
+    return true;
+}
 
 /* 设置IP和端口 */
 bool CurlFtp::setFtpIPAndPort(const std::string& IP, const int port)
@@ -558,15 +466,10 @@ void CurlFtp::setCaCertFile(const std::string& caCertFile)
     m_caCertFile = caCertFile;
 }
 
-
-
-/* 设置用户名和密码 */
-bool CurlFtp::setUsernameAndPassword(const std::string& username, const std::string& password)
+/* 设置是否启用CURL的调试信息 */
+void CurlFtp::enableCurlDebug(bool isPrint)
 {
-    m_username = username;
-    m_password = password;
-
-    return true;
+    m_enableCurlDebug = isPrint;
 }
 
 
@@ -668,6 +571,18 @@ bool CurlFtp::isDirExist(const std::string& dir)
     /* 获取文件夹是否存在,不需要接收文件 */
     curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
 
+    // 禁用被动模式(如果需要)
+    curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
+
+    if(m_enableCurlDebug)
+    {
+        /* 启用调试信息 */
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
+
+    /* 启用持久连接 */
+    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
+
     CURLcode res = curl_easy_perform(curl);
     bool result = true;
     if(res != CURLE_OK)
@@ -675,6 +590,11 @@ bool CurlFtp::isDirExist(const std::string& dir)
         // SPDLOG_ERROR("Failed to get file list, error code: {} ,{}", (int)res, curl_easy_strerror(res));
         result = false;
     }
+    // bool ret = performCurl(curl);
+    // if(!ret)
+    // {
+    //     SPDLOG_ERROR("Failed to create FTP Dir");
+    // }
 
     return result;
 }
@@ -727,19 +647,27 @@ bool CurlFtp::createDirectory(const std::string& ftpDir)
     curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
     curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
 
-    CURLcode res = curl_easy_perform(curl);
-    bool result = true;
-    if(res != CURLE_OK) 
+    // 禁用被动模式(如果需要)
+    // curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
+
+    if(m_enableCurlDebug)
     {
-        SPDLOG_ERROR("Failed to create FTP Dir, error code: {} ,{}", (int)res, curl_easy_strerror(res));
-        result = false;
-    }else
+        /* 启用调试信息 */
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
+    // 启用持久连接
+    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
+
+    // CURLcode res = curl_easy_perform(curl);
+    bool ret = performCurl(curl);
+    if(!ret)
     {
-        result = true;
+        SPDLOG_ERROR("Failed to create FTP Dir");
     }
+
     curl_easy_cleanup(curl);
     curl_slist_free_all(headerlist);
-    return result;
+    return ret;
 }
 
 /* 创建FTP文件夹,递归创建 */
@@ -786,8 +714,16 @@ bool CurlFtp::createDirectories(const std::string& ftpDir)
 
 
 
-/* 下载文件 */
-bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& localFile)
+/**
+ * @brief 下载文件
+ * 
+ * @param remoteFile 
+ * @param localFile 
+ * @param timeout 超时时间,单位秒
+ * @return true 
+ * @return false 
+ */
+bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& localFile, size_t timeout)
 {
     if(m_ftpUrl.empty())
     {
@@ -822,16 +758,17 @@ bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& loc
     curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.c_str());
     curl_easy_setopt(curl, CURLOPT_PORT, m_port);
     /* 设置用户名和密码 */
-    curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
-    curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
+    // curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
+    // curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
+    curl_easy_setopt(curl, CURLOPT_USERPWD, (m_username + ":" + m_password).c_str());
     /* 启用跟随重定向 */
     curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
     /* 设置回调函数 */
-    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeDataCallBack);
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFileCallBack);
     curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ofs);
     /* 设置超时时间 */
     // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
-    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
+    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
     /* 设置进度回调函数 */
     curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
     curl_easy_setopt(curl, CURLOPT_XFERINFODATA, nullptr);
@@ -853,7 +790,15 @@ bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& loc
             curl_easy_setopt(curl, CURLOPT_CAINFO, m_caCertFile.c_str());
         }
     }
-
+    // 禁用被动模式(如果需要)
+    curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
+    if(m_enableCurlDebug)
+    {
+        /* 启用调试信息 */
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
+    /* 启用持久连接 */
+    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
     /* 发送请求 */
     CURLcode res = curl_easy_perform(curl);
     if(res != CURLE_OK)
@@ -876,15 +821,101 @@ bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& loc
     return true;
 }
 
+/* 下载文件到数组 */
+bool CurlFtp::downloadToArray(const std::string& remoteFile, std::vector<char>& arrayInfo, size_t timeout)
+{
+    if(m_ftpUrl.empty())
+    {
+        SPDLOG_ERROR("ftpUrl is empty");
+        return false;
+    }
+    
+    /* 检查传入的文件是否符合规范 */
+    std::string remoteFileTmp = checkFilePath(remoteFile);
+    std::string ftpUrl = m_ftpUrl + remoteFileTmp;
+
+    CURL* curl = nullptr;
+    curl = curl_easy_init();
+    if(curl == nullptr)
+    {
+        SPDLOG_ERROR("curl init failed !");
+        return false;
+    }
+
+    /* 设置FTP地址 */
+    curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.c_str());
+    curl_easy_setopt(curl, CURLOPT_PORT, m_port);
+    /* 设置用户名和密码 */
+    // curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
+    // curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
+    curl_easy_setopt(curl, CURLOPT_USERPWD, (m_username + ":" + m_password).c_str());
+    /* 启用跟随重定向 */
+    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+    /* 设置回调函数 */
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeDataCallBack);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &arrayInfo);
+    /* 设置超时时间 */
+    // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
+    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
+    /* 设置进度回调函数 */
+    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
+    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, nullptr);
+    /* 启用下载进度回调函数 */
+    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
+    /* 判断是否是SFTP */
+    if(m_isSftp)
+    {
+        /* 判断是否需要设置CA证书 */
+        if(m_isIgnoreSSLCert)
+        {
+            /* 忽略证书验证 */
+            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+            /* 忽略主机名验证 */
+            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
+        } else
+        {
+            /* 设置CA证书 */
+            curl_easy_setopt(curl, CURLOPT_CAINFO, m_caCertFile.c_str());
+        }
+    }
+    // 禁用被动模式(如果需要)
+    curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
+    if(m_enableCurlDebug)
+    {
+        /* 启用调试信息 */
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
+    /* 启用持久连接 */
+    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
+    /* 发送请求 */
+    CURLcode res = curl_easy_perform(curl);
+    if(res != CURLE_OK)
+    {
+        SPDLOG_ERROR("Failed to get file list, error code: {} ,{}", (int)res, curl_easy_strerror(res));
+        SPDLOG_ERROR("Url = {}", ftpUrl);
+        /* 清理下载失败的文件 */
+        return false;
+    }
+    /* 清理curl */
+    curl_easy_cleanup(curl);
+
+    /* 打印一个换行符 */
+    // printf("\n");
+    printf("\n");
+
+    return true;
+}
+
 /**
  * @brief 上传文件
  * 
  * @param localFile 本地文件
  * @param remoteFile 远程文件
+ * @param timeout 超时时间,单位秒
  * @return true 
  * @return false 
  */
-bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remoteFile)
+bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remoteFile, size_t timeout, bool isCreateDir)
 {
     if(m_ftpUrl.empty())
     {
@@ -903,12 +934,19 @@ bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remote
     /* 检查远程FTP上的文件夹是否存在,不存在则创建 */
     if(!isDirExist(remoteDirTmp))
     {
-        if(!createDirectories(remoteDirTmp))
+        if(isCreateDir)
         {
-            // SPDLOG_ERROR("Failed to create remote dir: {}", remoteFileTmp);
+            if(!createDirectories(remoteDirTmp))
+            {
+                // SPDLOG_ERROR("Failed to create remote dir: {}", remoteFileTmp);
+                return false;
+            }
+        }else {
+            SPDLOG_ERROR("Remote dir is not exist: {}", remoteDirTmp);
             return false;
         }
     }
+        
     /* 拼接远程文件的url */
     std::string ftpUrl = m_ftpUrl + remoteFileTmp;
     
@@ -947,18 +985,28 @@ bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remote
     /* 启用上传 */
     curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
     /* 设置回调函数 */
-    curl_easy_setopt(curl, CURLOPT_READFUNCTION, readDataCallBack);
+    curl_easy_setopt(curl, CURLOPT_READFUNCTION, readFileCallBack);
     curl_easy_setopt(curl, CURLOPT_READDATA, &ifs);
     /* 设置上传文件的大小 */
     curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
     /* 设置超时时间 */
     // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
-    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
+    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
     /* 设置进度回调函数 */
     curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
     curl_easy_setopt(curl, CURLOPT_XFERINFODATA, nullptr);
     /* 启用下载进度回调函数 */
     curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
+
+    // 禁用被动模式(如果需要)
+    curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
+    if(m_enableCurlDebug)
+    {
+        /* 启用调试信息 */
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
+    /* 启用持久连接 */
+    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
     /* 发送请求 */
     CURLcode res = curl_easy_perform(curl);
     bool result = true;
@@ -978,6 +1026,110 @@ bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remote
 }
 
 
+/**
+ * @brief 上传文件,上传数据
+ *        注意:函数内不会拷贝数据,因此在上传完成前需要保证该指针的有效性,拷贝完成后也不会销毁源数据
+ *        默认超时时间是30秒,也无法设置
+ * 
+ * @param srcData 数据指针
+ * @param size 数据大小
+ * @param remoteFile 远程文件名,包括地址
+ * @param timeout 超时时间,单位秒
+ * @return true 
+ * @return false 
+ */
+bool CurlFtp::uploadData(char* srcData, size_t size, const std::string& remoteFile, size_t timeout, bool isCreateDir)
+{
+    if(m_ftpUrl.empty())
+    {
+        SPDLOG_ERROR("Url is empty");
+        return false;
+    }
+
+    /* 初始化本地数据 */
+    CF_ArrayInfo arrayInfo;
+    arrayInfo.data = srcData;
+    arrayInfo.size = size;
+    arrayInfo.pos = 0;
+    /* 检查FTP文件名是否符合规范 */
+    std::string remoteFileTmp = checkFilePath(remoteFile);
+    std::string remoteDirTmp = remoteFileTmp.substr(0, remoteFileTmp.find_last_of("/"));
+    /* 检查远程FTP上的文件夹是否存在,不存在则创建 */
+    if(!isDirExist(remoteDirTmp))
+    {
+        if(isCreateDir)
+        {
+            if(!createDirectories(remoteDirTmp))
+            {
+                // SPDLOG_ERROR("Failed to create remote dir: {}", remoteFileTmp);
+                return false;
+            }
+        }else {
+            SPDLOG_ERROR("Remote dir is not exist: {}", remoteDirTmp);
+            return false;
+        }
+    }
+    /* 拼接远程文件的url */
+    std::string ftpUrl = m_ftpUrl + remoteFileTmp;
+    SPDLOG_DEBUG("Data size: {}", arrayInfo.size);
+
+    CURL* curl = nullptr;
+    curl = curl_easy_init();
+    if(curl == nullptr)
+    {
+        SPDLOG_ERROR("curl init failed !");
+        return false;
+    }
+    /* 设置FTP地址 */
+    curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.c_str());
+    curl_easy_setopt(curl, CURLOPT_PORT, m_port);
+    /* 设置用户名和密码 */
+    // curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
+    // curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
+    curl_easy_setopt(curl, CURLOPT_USERPWD, (m_username + ":" + m_password).c_str());
+    /* 启用跟随重定向 */
+    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+    /* 启用上传 */
+    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+    /* 设置回调函数 */
+    curl_easy_setopt(curl, CURLOPT_READFUNCTION, readDataCallBack);
+    curl_easy_setopt(curl, CURLOPT_READDATA, &arrayInfo);
+    /* 设置上传文件的大小 */
+    curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)arrayInfo.size);
+    /* 设置超时时间 */
+    // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
+    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
+    /* 设置进度回调函数 */
+    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
+    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, nullptr);
+    /* 启用下载进度回调函数 */
+    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
+
+    // 禁用被动模式(如果需要)
+    curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
+    if(m_enableCurlDebug)
+    {
+        /* 启用调试信息 */
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
+    /* 启用持久连接 */
+    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
+    /* 发送请求 */
+    CURLcode res = curl_easy_perform(curl);
+    bool result = true;
+    if(res != CURLE_OK)
+    {
+        SPDLOG_ERROR("Upload file failed, error code: {} ,{}", (int)res, curl_easy_strerror(res));
+        SPDLOG_ERROR("Url = {}", ftpUrl);
+        result = false;
+    }
+    /* 关闭文件,清理curl */
+    curl_easy_cleanup(curl);
+    /* 打印一个换行符 */
+    printf("\n");
+
+    return result;
+}
 
 
 
@@ -1023,6 +1175,13 @@ bool CurlFtp::listAll(CURL* curl, std::string dir, std::vector<CF_FileInfo>& fil
     curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
     curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
 
+    // 禁用被动模式(如果需要)
+    curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
+    if(m_enableCurlDebug)
+    {
+        /* 启用调试信息 */
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    }
     /* 发送请求 */
     CURLcode res = curl_easy_perform(curl);
     if(res != CURLE_OK)
@@ -1115,5 +1274,26 @@ bool CurlFtp::checkLocalDir(const std::string& localDir)
 }
 
 
+/* 执行curl,添加重试机制 */
+bool CurlFtp::performCurl(CURL* curl)
+{
+    int retry = 3;
+    while(retry > 0)
+    {
+        CURLcode res = curl_easy_perform(curl);
+        if(res == CURLE_OK)
+        {
+            return true;
+        }else
+        {
+            SPDLOG_ERROR("Perform curl failed, error code: {} ,{}", (int)res, curl_easy_strerror(res));
+            SPDLOG_ERROR("Retry times: {}", 3 - retry);
+            retry--;
+        }
+    }
+
+    return false;
+}
+
 
 

+ 14 - 4
common/CurlFtp/CurlFtp.h

@@ -33,6 +33,8 @@ public:
     // static bool createDir(const std::string &ftpUrl, const std::string &username, const std::string &password, const std::string &dirName);
 
     /*********************** 成员函数 *********************/
+    /* 设置用户名和密码 */
+    bool setUsernameAndPassword(const std::string& username, const std::string& password);
     /* 设置IP和端口 */
     bool setFtpIPAndPort(const std::string& IP, const int port);
     /* 设置SFTP的IP和端口 */
@@ -41,9 +43,10 @@ public:
     void setIgnoreSSLCert(bool isIgnore);
     /* 设置CA证书文件 */
     void setCaCertFile(const std::string& caCertFile);
+    /* 设置是否启用CURL的调试信息 */
+    void enableCurlDebug(bool isPrint = false);
 
-    /* 设置用户名和密码 */
-    bool setUsernameAndPassword(const std::string& username, const std::string& password);
+    
     /* 列出文件列表 */
     bool getFileList(std::string dir, std::vector<std::string>& fileList);
     /* 获取文件夹列表 */
@@ -56,9 +59,13 @@ public:
     bool createDirectories(const std::string& ftpDir);
 
     /* 下载文件 */
-    bool downloadFile(const std::string& remoteFile, const std::string& localFile);
+    bool downloadFile(const std::string& remoteFile, const std::string& localFile, size_t timeout = 30);
+    /* 下载文件到数组 */
+    bool downloadToArray(const std::string& remoteFile,std::vector<char>& arrayInfo, size_t timeout = 30);
     /* 上传文件 */
-    bool uploadFile(const std::string& localFile, const std::string& remoteFile);
+    bool uploadFile(const std::string& localFile, const std::string& remoteFile, size_t timeout = 30, bool isCreateDir = false);
+    /* 上传文件,上传数据 */
+    bool uploadData(char* srcData, size_t size, const std::string& remoteFile, size_t timeout = 30, bool isCreateDir = false);
 
 private:
     /* 列出所有内容 */
@@ -69,6 +76,8 @@ private:
     std::string checkFilePath(const std::string& file);
     /* 检查本地文件夹是否存在,不存在则创建 */
     bool checkLocalDir(const std::string& localDir);
+    /* 执行curl,添加重试机制 */
+    bool performCurl(CURL* curl);
 
 private:
     std::mutex m_mutexCurl;                 /* curl互斥锁 */
@@ -83,6 +92,7 @@ private:
 
     bool m_isSftp = false;                  /* 是否是SFTP */
     bool m_isIgnoreSSLCert = false;         /* 是否忽略SSL证书 */
+    bool m_enableCurlDebug = false;         /* 是否打印调试信息 */
 
     std::string m_caCertFile;               /* CA证书文件 */
 };

+ 11 - 0
common/CurlFtp/CurlFtpInfo.h

@@ -45,6 +45,17 @@ struct CF_FileInfo
     }
 };
 
+/**
+ * @brief 数组信息,上传下载时使用
+ * 
+ */
+struct CF_ArrayInfo
+{
+    uint64_t size = 0;              /* 数组大小 */
+    uint64_t pos = 0;               /* 当前位置 */
+    char* data = nullptr;           /* 数据 */
+};
+
 
 
 #endif /* CURLFTPINFO_H */