CurlFtp.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327
  1. #include "CurlFtp.h"
  2. #include <regex>
  3. #include <iosfwd>
  4. #include <filesystem>
  5. #include <fstream>
  6. // #include "fmtlog.h"
  7. #include "spdlog/spdlog.h"
  8. #ifndef SPDLOG_ACTIVE_LEVEL
  9. #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
  10. #endif
  11. #if defined(_WIN32)
  12. #endif /* _WIN32 */
  13. /* ==================================================================================
  14. * *********************************** 全局变量 *************************************
  15. * ==================================================================================
  16. */
  17. /* 是否有初始化实例,主要用于静态函数调用curl_global_cleanup()函数前判断,有实例就不调用 */
  18. static bool hasInstace = false;
  19. /* 记录上传或者下载的时间,thread_local是C++11加入的关键字,表示在每个线程中都有自己的lastTime */
  20. static thread_local std::chrono::system_clock::time_point lastTime;
  21. static thread_local uint64_t dCount;
  22. static thread_local uint64_t uCount;
  23. static thread_local uint64_t dSpeed; /* 保存最后的下载速度 */
  24. static thread_local uint64_t uSpeed; /* 保存最后的上传速度 */
  25. /* ==================================================================================
  26. * *********************************** 全局函数 *************************************
  27. * ==================================================================================
  28. */
  29. /**
  30. * @brief 写入回调函数
  31. *
  32. * @param contents curl的数据缓冲区
  33. * @param size 数据大小,单位是size_t
  34. * @param nmemb size_t的单位字节数
  35. * @param userStr 用户提供的字符串,格式可以是任意的
  36. * @return size_t 总共获取到的数据大小,单位是字节
  37. */
  38. static size_t writeStringCallback(void *contents, size_t size, size_t nmemb, std::string *userStr)
  39. {
  40. size_t newLength = size * nmemb;
  41. size_t oldLength = userStr->size();
  42. try
  43. {
  44. userStr->resize(oldLength + newLength);
  45. }
  46. catch(std::bad_alloc &e)
  47. {
  48. //handle memory problem
  49. return 0;
  50. }
  51. std::copy_n((char*)contents, newLength, userStr->begin() + oldLength);
  52. return size * nmemb;
  53. }
  54. /**
  55. * @brief 写入回调函数,listFiles需要调用
  56. *
  57. * @param buffer curl下载回来的数据
  58. * @param size 数据大小
  59. * @param nmemb 数据单位字节数
  60. * @param userp 用户传进来的容器
  61. * @return int 返回拷贝的字节数
  62. */
  63. static int writeStringListCallback(void* buffer, size_t size, size_t nmemb, void* userp)
  64. {
  65. std::vector<std::string>* fileList = static_cast<std::vector<std::string>*>(userp);
  66. std::string line(static_cast<char*>(buffer), size * nmemb);
  67. // printf("line = %s\n", line.c_str());
  68. fileList->push_back(line);
  69. return size * nmemb;
  70. }
  71. /**
  72. * @brief 写入文件回调函数
  73. *
  74. * @param contents 读取到的数据内容
  75. * @param size 数据大小
  76. * @param nmemb 数据单位
  77. * @param pFile 文件指针
  78. * @return size_t 实际读取的大小
  79. */
  80. static size_t writeFileCallBack(void* contents, size_t size, size_t nmemb, std::ostream* pFile)
  81. {
  82. pFile->write(reinterpret_cast<char*>(contents), size * nmemb);
  83. return size * nmemb;
  84. }
  85. /**
  86. * @brief 写入数据到vector中
  87. *
  88. * @param contents
  89. * @param size
  90. * @param nmemb
  91. * @param vecData
  92. * @return size_t
  93. */
  94. static size_t writeDataCallBack(void* contents, size_t size, size_t nmemb, std::vector<char>* vecData)
  95. {
  96. size_t copySize = size * nmemb;
  97. vecData->insert(vecData->end(), (char*)contents, (char*)contents + copySize);
  98. return copySize;
  99. }
  100. /**
  101. * @brief 读取文件回调函数
  102. *
  103. * @param contents 下载到的数据内容
  104. * @param size 数据大小
  105. * @param nmemb 数据单位
  106. * @param pFile 文件指针
  107. * @return size_t 写入的大小
  108. */
  109. static size_t readFileCallBack(void* contents, size_t size, size_t nmemb, std::istream* pFile)
  110. {
  111. pFile->read(reinterpret_cast<char*>(contents), size * nmemb);
  112. /* 获取读取到的字节数,可能读取到文件末尾,所以不能直接使用传入的字节数 */
  113. size_t readSize = pFile->gcount();
  114. return readSize;
  115. }
  116. /**
  117. * @brief
  118. *
  119. * @param contents ftp需要的目标内容
  120. * @param size 拷贝的数据大小
  121. * @param nmemb 单个数据的字节数
  122. * @param pData 源指针
  123. * @return size_t 已拷贝的数据大小
  124. */
  125. static size_t readDataCallBack(void* contents, size_t size, size_t nmemb, CF_ArrayInfo* pData)
  126. {
  127. if(pData == nullptr)
  128. {
  129. return 0;
  130. }
  131. /* 判断是否还够本次拷贝的字节数 */
  132. size_t copySize = size * nmemb;
  133. if(pData->size - pData->pos < copySize)
  134. {
  135. copySize = pData->size - pData->pos;
  136. }
  137. memcpy(contents, pData->data + pData->pos, copySize);
  138. pData->pos += copySize;
  139. return copySize;
  140. }
  141. /**
  142. * @brief 计算速度
  143. *
  144. * @param speed
  145. * @param retSpeed
  146. * @param unit
  147. */
  148. void computeSpeed(uint64_t speed, double& retSpeed, std::string& unit)
  149. {
  150. double KB = speed / 1024.0;
  151. double MB = KB / 1024.0;
  152. double GB = MB / 1024.0;
  153. if(GB > 1)
  154. {
  155. unit = "GB/S";
  156. retSpeed = GB;
  157. }
  158. else if(MB > 1)
  159. {
  160. unit = "MB/S";
  161. retSpeed = MB;
  162. }
  163. else if(KB > 1)
  164. {
  165. unit = "KB/S";
  166. retSpeed = KB;
  167. }
  168. else {
  169. unit = "B/S";
  170. retSpeed = speed;
  171. }
  172. }
  173. /**
  174. * @brief 打印进度条
  175. *
  176. * @param type 上传还是下载类型
  177. * @param total 总大小,单位字节
  178. * @param now 现在的进度,单位字节
  179. */
  180. void printProgress(CF_TransType type, curl_off_t total, curl_off_t now)
  181. {
  182. std::string transType;
  183. uint64_t count = 0;
  184. if(type == CF_TransType::DOWNLOAD)
  185. {
  186. transType = "Download";
  187. count = now - dCount;
  188. dCount = now;
  189. }
  190. else if (type == CF_TransType::UPLOAD)
  191. {
  192. transType = "Upload";
  193. count = now - uCount;
  194. uCount = now;
  195. }
  196. /* 计算进度,百分比 */
  197. double percent = (double)now / (double)total * 100;
  198. /* 计算单位 */
  199. double tKB = total / 1024.0;
  200. double tMB = tKB / 1024.0;
  201. double tGB = tMB / 1024.0;
  202. /* 计算速度 */
  203. double speed = 0.0;
  204. std::string unit;
  205. computeSpeed(count, speed, unit);
  206. // if(speed == 0.0)
  207. // {
  208. // if(CF_TransType::DOWNLOAD == type)
  209. // {
  210. // speed = dSpeed;
  211. // }else if (CF_TransType::UPLOAD == type) {
  212. // speed = uSpeed;
  213. // }
  214. // }else {
  215. // if(CF_TransType::DOWNLOAD == type)
  216. // {
  217. // dSpeed = speed;
  218. // }else if (CF_TransType::UPLOAD == type) {
  219. // uSpeed = speed;
  220. // }
  221. // }
  222. if(tGB > 1)
  223. {
  224. double dGB = now / 1024.0 / 1024.0 / 1024.0;
  225. double speed = count / 1024.0 / 1024.0;
  226. printf("%s Total / now : %.2fGB / %.2fGB, %.2f%%, Speed:%.2f %s\r", transType.c_str(), tGB, dGB, percent, speed, unit.c_str());
  227. // printf("Download Total / now : {:.2f}GB / {:.2f}GB, {:.2f}%\r", tGB, dGB, percent);
  228. }
  229. else if(tMB > 1)
  230. {
  231. double dMB = now / 1024.0 / 1024.0;
  232. printf("%s Total / now : %.2fMB / %.2fMB, %.2f%%, Speed:%.2f %s\r", transType.c_str(), tMB, dMB, percent, speed, unit.c_str());
  233. // printf("Download Total / now : {:.2f}MB / {:.2f}MB, {:.2f}%\r", tMB, dMB, percent);
  234. }
  235. else if(tKB > 1)
  236. {
  237. double dKB = now / 1024.0;
  238. printf("%s Total / now : %.2fKB / %.2fKB, %.2f%%, Speed:%.2f %s\r", transType.c_str(), tKB, dKB, percent, speed, unit.c_str());
  239. }
  240. else
  241. {
  242. printf("%s Total / now : %ldB / %ldB, %.2f%%, Speed:%.2f %s\r", transType.c_str(), total, now, percent, speed, unit.c_str());
  243. }
  244. fflush(stdout);
  245. }
  246. /**
  247. * @brief 上传和下载进度回调函数,这个函数kennel会被多次调用,即使是没有在下载的时候,因此需要判断传入的数据是否是0
  248. * 必须将 CURLOPT_NOPROGRESS 设为 0 才能真正调用该函数。
  249. *
  250. * @param clientp 通过CURLOPT_XFERINFODATA 设置的指针,libcurl 不会使用它,只会将其从应用程序传递给回调函数。
  251. * 可以通过这个指针将下载的进度传递给应用程序
  252. * @param dltotal 下载的总字节数,上传的时候这个为0
  253. * @param dlnow 已经下载的总字节数
  254. * @param ultotal 需要上传的总字节数,下载的时候这个为0
  255. * @param ulnow 已经上传的总字节数
  256. * @return int 返回0表示正常,非0表示异常
  257. */
  258. static int progress_callback(void *clientp,
  259. curl_off_t dltotal,
  260. curl_off_t dlnow,
  261. curl_off_t ultotal,
  262. curl_off_t ulnow)
  263. {
  264. // SPDLOG_DEBUG("dTotal: {}, dNow: {}, uTotal: {}, uNow: {}", dltotal, dlnow, ultotal, ulnow);
  265. if(dltotal == 0 && ultotal == 0)
  266. {
  267. dCount = 0;
  268. uCount = 0;
  269. dSpeed = 0;
  270. uSpeed = 0;
  271. return 0;
  272. }
  273. std::chrono::system_clock::time_point nowTime = std::chrono::system_clock::now();
  274. auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - lastTime).count();
  275. // SPDLOG_DEBUG("duration:{}", duration);
  276. if((duration < 1000) && ((dltotal != dlnow) || (ultotal != ulnow)))
  277. {
  278. return 0;
  279. }
  280. lastTime = nowTime;
  281. /* 正在下载 */
  282. if(dltotal > 0)
  283. {
  284. printProgress(CF_TransType::DOWNLOAD, dltotal, dlnow);
  285. // printf("Download Total / now : {} / {}, {:.2f}%\r", dltotal, dlnow, downloadPercent);
  286. }
  287. /* 正在上传 */
  288. else if(ultotal > 0)
  289. {
  290. printProgress(CF_TransType::UPLOAD, ultotal, ulnow);
  291. // double uploadPercent = (double)ulnow / (double)ultotal * 100;
  292. // printf("Upload Total / now : {} / {}, {:.2f}%\r", ultotal, ulnow, uploadPercent);
  293. }
  294. return 0;
  295. }
  296. /* 使用Windows API进行编码转换 */
  297. #if defined(_WIN32)
  298. static char* GBToUTF8(const char* gb2312)
  299. {
  300. int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);
  301. wchar_t* wstr = new wchar_t[len+1];
  302. memset(wstr, 0, len+1);
  303. MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);
  304. len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
  305. char* str = new char[len+1];
  306. memset(str, 0, len+1);
  307. WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
  308. if(wstr) delete[] wstr;
  309. return str;
  310. }
  311. #endif /* _WIN32 */
  312. /**
  313. * @brief 解析字符串文件信息,如果FTP服务器规定好个编码,不需要进行转换
  314. *
  315. * @param strSrc 读取到的字符串
  316. * @param fileList 文件信息列表
  317. * @return true
  318. * @return false
  319. */
  320. static bool parseFileInfo(const std::string& strSrc, std::vector<CF_FileInfo>& fileInfo)
  321. {
  322. #if defined(_WIN32)
  323. // auto str1 = GBToUTF8(strSrc.c_str());
  324. std::string str2 = strSrc;
  325. #else
  326. std::string str2 = strSrc;
  327. #endif /* _WIN32 */
  328. // SPDLOG_DEBUG("\n{}", str2);
  329. /* 正则表达式,匹配多个空格 */
  330. std::regex reg(R"(( )+)");
  331. /* 匹配以非空格开头的字符,空格隔开,中间任意字符,空格隔开,非空格组成的结尾
  332. * (文件类型) ... (文件大小,$5) ... (文件名)
  333. */
  334. std::regex reg1(R"(^([^ ]+) (\d*) (\w*) (\w*) (\d*) (.*) ([^ ]+)$)");
  335. // SPDLOG_INFO("\n-------------------\n");
  336. /* 匹配换行符,分割每一行 */
  337. std::regex reg2(R"(\n)");
  338. /* 匹配文件夹 */
  339. std::regex reg3(R"(^d.*)");
  340. /* 匹配文件 */
  341. std::regex reg4(R"(^-.*$)");
  342. /* -1 表示对正则表达式之前的子序列感兴趣
  343. * 0 表示对正则表达式本身感兴趣,输出的就是换行符 */
  344. std::sregex_token_iterator it2(str2.begin(), str2.end(), reg2, -1);
  345. /* 这里取出每一行 */
  346. for(; it2 != std::sregex_token_iterator(); ++it2)
  347. {
  348. /* 去掉多余的空格 */
  349. auto line = std::regex_replace(it2->str(), reg, " ");
  350. // SPDLOG_INFO("{}", line);
  351. CF_FileInfo fi;
  352. /* 取出文件类型 */
  353. std::string strFileType = std::regex_replace(line, reg1, "$1");
  354. if(std::regex_match(strFileType, reg3))
  355. {
  356. fi.type = CF_FileType::DIR;
  357. }else if (std::regex_match(strFileType, reg4))
  358. {
  359. fi.type = CF_FileType::FILE;
  360. }
  361. /* 取出文件大小 */
  362. std::string strFileSize = std::regex_replace(line, reg1, "$5");
  363. fi.size = std::stoull(strFileSize);
  364. /* 取出文件名 */
  365. std::string strFileName = std::regex_replace(line, reg1, "$7");
  366. fi.name = strFileName;
  367. /* 加入队列 */
  368. fileInfo.push_back(fi);
  369. }
  370. return true;
  371. }
  372. /* ==================================================================================
  373. * *********************************** 成员函数 *************************************
  374. * ================================================================================== */
  375. CurlFtp::CurlFtp()
  376. {
  377. /* 调用初始化函数,这个函数可以重复调用 */
  378. curl_global_init(CURL_GLOBAL_DEFAULT);
  379. /* 初始化curl */
  380. hasInstace = true;
  381. }
  382. CurlFtp::~CurlFtp()
  383. {
  384. /* 清理curl */
  385. curl_easy_cleanup(m_curl);
  386. curl_global_cleanup();
  387. hasInstace = false;
  388. }
  389. /* 设置用户名和密码 */
  390. bool CurlFtp::setUsernameAndPassword(const std::string& username, const std::string& password)
  391. {
  392. m_username = username;
  393. m_password = password;
  394. return true;
  395. }
  396. /* 设置IP和端口 */
  397. bool CurlFtp::setFtpIPAndPort(const std::string& IP, const int port)
  398. {
  399. /*先判断是否有ftp器前缀,通过正则表达式,取出IP地址和端口号,去掉前缀和后面的'/ */
  400. m_IP = IP;
  401. m_port = port;
  402. m_ftpUrl = "ftp://" + m_IP + ":" + std::to_string(m_port);
  403. SPDLOG_INFO("Set ftpUrl = {}", m_ftpUrl);
  404. m_isSftp = false;
  405. return true;
  406. }
  407. /* 设置SFTP的IP和端口 */
  408. bool CurlFtp::setSftpIPAndPort(const std::string& IP, const int port)
  409. {
  410. m_IP = IP;
  411. m_port = port;
  412. m_ftpUrl = "sftp://" + m_IP + ":" + std::to_string(m_port);
  413. SPDLOG_INFO("Set sftpUrl = {}", m_ftpUrl);
  414. m_isSftp = true;
  415. return true;
  416. }
  417. /* 设置是否忽略SSL证书,使用SFTP不建议忽略 */
  418. void CurlFtp::setIgnoreSSLCert(bool isIgnore)
  419. {
  420. m_isIgnoreSSLCert = isIgnore;
  421. }
  422. /* 设置CA证书文件 */
  423. void CurlFtp::setCaCertFile(const std::string& caCertFile)
  424. {
  425. m_caCertFile = caCertFile;
  426. }
  427. /* 设置是否启用CURL的调试信息 */
  428. void CurlFtp::enableCurlDebug(bool isPrint)
  429. {
  430. m_enableCurlDebug = isPrint;
  431. }
  432. /* 列出文件列表 */
  433. bool CurlFtp::getFileList(std::string dir, std::vector<std::string>& fileList)
  434. {
  435. if(m_IP.empty())
  436. {
  437. SPDLOG_WARN("IP or port is empty");
  438. return false;
  439. }
  440. /* 检查dir,添加前缀“/” */
  441. auto dirTmp = checkDirPath(dir);
  442. // CURL *curl = nullptr;
  443. // curl = curl_easy_init();
  444. // if(curl == nullptr)
  445. // {
  446. // SPDLOG_ERROR("curl init failed !");
  447. // return false;
  448. // }
  449. resetCurl(m_curl);
  450. std::vector<CF_FileInfo> listInfo;
  451. /* 获取文件信息 */
  452. listAll(m_curl, dirTmp, listInfo);
  453. for(const CF_FileInfo& fi : listInfo)
  454. {
  455. // SPDLOG_INFO("type = {}, size = {}, name = {}", (int)fi.type, fi.size, fi.name);
  456. if(fi.type == CF_FileType::FILE)
  457. {
  458. fileList.push_back(fi.name);
  459. }
  460. }
  461. // curl_easy_cleanup(curl);
  462. return true;
  463. }
  464. /* 获取文件夹列表 */
  465. bool CurlFtp::getDirList(std::string dir, std::vector<std::string>& dirList)
  466. {
  467. if(m_IP.empty())
  468. {
  469. SPDLOG_WARN("IP or port is empty");
  470. return false;
  471. }
  472. /* 检查dir,添加前缀“/” */
  473. auto dirTmp = checkDirPath(dir);
  474. // CURL *curl = nullptr;
  475. // curl = curl_easy_init();
  476. // if(curl == nullptr)
  477. // {
  478. // SPDLOG_ERROR("curl init failed !");
  479. // return false;
  480. // }
  481. resetCurl(m_curl);
  482. std::vector<CF_FileInfo> listInfo;
  483. /* 获取文件信息 */
  484. listAll(m_curl, dirTmp, listInfo);
  485. for(const CF_FileInfo& fi : listInfo)
  486. {
  487. // SPDLOG_INFO("type = {}, size = {}, name = {}", (int)fi.type, fi.size, fi.name);
  488. if(fi.type == CF_FileType::DIR)
  489. {
  490. dirList.push_back(fi.name);
  491. }
  492. }
  493. // curl_easy_cleanup(curl);
  494. return true;
  495. }
  496. /* 判断文件夹是否存在 */
  497. bool CurlFtp::isDirExist(const std::string& dir)
  498. {
  499. SPDLOG_DEBUG("Check dir: {}", dir);
  500. if(m_ftpUrl.empty())
  501. {
  502. SPDLOG_ERROR("ftpUrl is empty");
  503. return false;
  504. }
  505. /* 检查传入的文件夹 */
  506. auto dirTmp = checkDirPath(dir);
  507. // CURL* curl = nullptr;
  508. resetCurl(m_curl);
  509. std::string ftpUrl = m_ftpUrl + dirTmp;
  510. curl_easy_setopt(m_curl, CURLOPT_URL, ftpUrl.c_str());
  511. curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
  512. curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
  513. /* 获取文件夹是否存在,不需要接收文件 */
  514. curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
  515. /* 设置SFTP */
  516. setSftp(m_curl);
  517. /* 启用调试信息 */
  518. if(m_enableCurlDebug)
  519. {
  520. curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
  521. }
  522. /* 启用持久连接 */
  523. // curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
  524. /* 这里只需要执行一次 */
  525. CURLcode res = curl_easy_perform(m_curl);
  526. bool result = true;
  527. if(res != CURLE_OK)
  528. {
  529. // SPDLOG_ERROR("Failed to get file list, error code: {} ,{}", (int)res, curl_easy_strerror(res));
  530. result = false;
  531. }
  532. // bool ret = performCurl(m_curl);
  533. // if(!ret)
  534. // {
  535. // // SPDLOG_ERROR("Failed to create FTP Dir");
  536. // }
  537. return result;
  538. }
  539. /**
  540. * @brief 创建FTP文件夹,只能创建一层文件夹
  541. *
  542. * @param ftpDir 文件夹路径
  543. * @return true 文件夹创建成功或者文件夹存在
  544. * @return false
  545. */
  546. bool CurlFtp::createDirectory(const std::string& ftpDir)
  547. {
  548. if(m_ftpUrl.empty())
  549. {
  550. SPDLOG_ERROR("ftpUrl is empty");
  551. return false;
  552. }
  553. /* 先检查FTP文件夹是否存在,如果存在直接返回true */
  554. if(isDirExist(ftpDir))
  555. {
  556. return true;
  557. }
  558. /* 检查传入的文件夹格式 */
  559. auto dirTmp = checkDirPath(ftpDir);
  560. // CURL *curl = curl_easy_init();
  561. // if(curl == nullptr)
  562. // {
  563. // SPDLOG_ERROR("Create FTP DIR, curl init failed !");
  564. // return false;
  565. // }
  566. if(m_curl == nullptr)
  567. {
  568. m_curl = curl_easy_init();
  569. if(m_curl == nullptr)
  570. {
  571. SPDLOG_ERROR("curl init failed !");
  572. return false;
  573. }
  574. }else {
  575. curl_easy_reset(m_curl);
  576. }
  577. curl_easy_setopt(m_curl, CURLOPT_URL, m_ftpUrl.c_str());
  578. curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
  579. curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
  580. /* 创建FTP头信息 */
  581. struct curl_slist *headerlist = NULL;
  582. std::string mkdir = "MKD " + dirTmp;
  583. headerlist = curl_slist_append(headerlist, mkdir.c_str());
  584. /* 设置FTP命令行选项 */
  585. curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
  586. /* 不包含实体 */
  587. curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
  588. /* 启用跟随重定向 */
  589. curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
  590. /* 设置超时时间 */
  591. curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, 30L);
  592. curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, 30L);
  593. /* 设置SFTP */
  594. setSftp(m_curl);
  595. if(m_enableCurlDebug)
  596. {
  597. /* 启用调试信息 */
  598. curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
  599. }
  600. // 启用持久连接
  601. // curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
  602. SPDLOG_DEBUG("Create dir: {}", dirTmp);
  603. // CURLcode res = curl_easy_perform(curl);
  604. bool ret = performCurl(m_curl);
  605. if(!ret)
  606. {
  607. SPDLOG_ERROR("Failed to create FTP Dir");
  608. }
  609. // curl_easy_cleanup(curl);
  610. curl_slist_free_all(headerlist);
  611. return ret;
  612. }
  613. /* 创建FTP文件夹,递归创建 */
  614. bool CurlFtp::createDirectories(const std::string& ftpDir)
  615. {
  616. /* 检查路径格式,并去掉第一个 / */
  617. std::string ftpDirTmp = checkDirPath(ftpDir);
  618. std::string ftpDir2 = ftpDirTmp.substr(1, ftpDirTmp.size() -1);
  619. /* 将文件夹分开,取出每一个文件夹名称 */
  620. std::vector<std::string> strList;
  621. std::regex reg(R"(/)");
  622. std::sregex_token_iterator it(ftpDir2.begin(), ftpDir2.end(), reg, -1);
  623. for( ; it != std::sregex_token_iterator(); ++it)
  624. {
  625. strList.push_back(it->str());
  626. }
  627. /* 将每一层拼接起来,逐层递归 */
  628. std::vector<std::string> dirList;
  629. for(const std::string& dir : strList)
  630. {
  631. std::string dirTmp = "/";
  632. if(!dirList.empty())
  633. {
  634. dirTmp = dirList.back();
  635. dirTmp += "/";
  636. }
  637. dirTmp += dir;
  638. dirList.push_back(dirTmp);
  639. }
  640. /* 逐层创建 */
  641. for(const std::string& dir : dirList)
  642. {
  643. // SPDLOG_DEBUG("Create dir: {}", dir);
  644. if(!createDirectory(dir))
  645. {
  646. SPDLOG_ERROR("Failed to create dir: {}", dir);
  647. return false;
  648. }
  649. }
  650. return true;
  651. }
  652. /**
  653. * @brief 下载文件
  654. *
  655. * @param remoteFile
  656. * @param localFile
  657. * @param timeout 超时时间,单位秒
  658. * @return true
  659. * @return false
  660. */
  661. bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& localFile, size_t timeout)
  662. {
  663. if(m_ftpUrl.empty())
  664. {
  665. SPDLOG_ERROR("ftpUrl is empty");
  666. return false;
  667. }
  668. /* 检查传入的文件是否符合规范 */
  669. std::string remoteFileTmp = checkFilePath(remoteFile);
  670. std::string ftpUrl = m_ftpUrl + remoteFileTmp;
  671. /* 检查本地文件夹是否存在,不存在则创建 */
  672. // std::string localDir = localFile.substr(0, localFile.find_last_of("/")); /* 去掉最后的 / */
  673. std::string localDirTmp = localFile.substr(0, localFile.find_last_of("/"));
  674. if(!checkLocalDir(localDirTmp))
  675. {
  676. SPDLOG_ERROR("Failed to create local dir: {}", localDirTmp);
  677. return false;
  678. }
  679. // CURL* curl = nullptr;
  680. // curl = curl_easy_init();
  681. // if(curl == nullptr)
  682. // {
  683. // SPDLOG_ERROR("curl init failed !");
  684. // return false;
  685. // }
  686. resetCurl(m_curl);
  687. /* 打开文件 */
  688. std::ofstream ofs;
  689. ofs.open(localFile, std::ios::out | std::ios::binary | std::ios::trunc);
  690. /* 设置FTP地址 */
  691. curl_easy_setopt(m_curl, CURLOPT_URL, ftpUrl.c_str());
  692. curl_easy_setopt(m_curl, CURLOPT_PORT, m_port);
  693. /* 设置用户名和密码 */
  694. curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
  695. curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
  696. curl_easy_setopt(m_curl, CURLOPT_USERPWD, (m_username + ":" + m_password).c_str());
  697. /* 启用跟随重定向 */
  698. curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
  699. /* 设置回调函数 */
  700. curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeFileCallBack);
  701. curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &ofs);
  702. /* 设置超时时间 */
  703. // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
  704. curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, timeout);
  705. /* 设置进度回调函数 */
  706. curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
  707. curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, nullptr);
  708. /* 启用下载进度回调函数 */
  709. curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
  710. /* 设置SFTP */
  711. setSftp(m_curl);
  712. if(m_enableCurlDebug)
  713. {
  714. /* 启用调试信息 */
  715. curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
  716. }
  717. /* 启用持久连接 */
  718. curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
  719. /* 发送请求 */
  720. bool ret = performCurl(m_curl);
  721. if(!ret)
  722. {
  723. SPDLOG_ERROR("Failed to get file list, Url = {}", ftpUrl);
  724. /* 清理下载失败的文件 */
  725. ofs.close();
  726. std::remove(localFile.c_str());
  727. return false;
  728. }
  729. /* 关闭文件,清理curl */
  730. ofs.close();
  731. // curl_easy_cleanup(curl);
  732. /* 打印一个换行符 */
  733. // printf("\n");
  734. printf("\n");
  735. return true;
  736. }
  737. /* 下载文件到数组 */
  738. bool CurlFtp::downloadToArray(const std::string& remoteFile, std::vector<char>& arrayInfo, size_t timeout)
  739. {
  740. if(m_ftpUrl.empty())
  741. {
  742. SPDLOG_ERROR("ftpUrl is empty");
  743. return false;
  744. }
  745. /* 检查传入的文件是否符合规范 */
  746. std::string remoteFileTmp = checkFilePath(remoteFile);
  747. std::string ftpUrl = m_ftpUrl + remoteFileTmp;
  748. // CURL* curl = nullptr;
  749. // curl = curl_easy_init();
  750. // if(curl == nullptr)
  751. // {
  752. // SPDLOG_ERROR("curl init failed !");
  753. // return false;
  754. // }
  755. resetCurl(m_curl);
  756. /* 设置FTP地址 */
  757. curl_easy_setopt(m_curl, CURLOPT_URL, ftpUrl.c_str());
  758. curl_easy_setopt(m_curl, CURLOPT_PORT, m_port);
  759. /* 设置用户名和密码 */
  760. curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
  761. curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
  762. curl_easy_setopt(m_curl, CURLOPT_USERPWD, (m_username + ":" + m_password).c_str());
  763. /* 启用跟随重定向 */
  764. curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
  765. /* 设置回调函数 */
  766. curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeDataCallBack);
  767. curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &arrayInfo);
  768. /* 设置超时时间 */
  769. // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
  770. curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, timeout);
  771. /* 设置进度回调函数 */
  772. curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
  773. curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, nullptr);
  774. /* 启用下载进度回调函数 */
  775. curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
  776. /* 设置SFTP */
  777. setSftp(m_curl);
  778. if(m_enableCurlDebug)
  779. {
  780. /* 启用调试信息 */
  781. curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
  782. }
  783. /* 启用持久连接 */
  784. curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
  785. /* 发送请求 */
  786. bool ret = performCurl(m_curl);
  787. if(!ret)
  788. {
  789. SPDLOG_ERROR("Failed to get file list, Url = {}", ftpUrl);
  790. }
  791. /* 清理curl */
  792. // curl_easy_cleanup(curl);
  793. /* 打印一个换行符 */
  794. // printf("\n");
  795. printf("\n");
  796. return ret;
  797. }
  798. /**
  799. * @brief 上传文件
  800. *
  801. * @param localFile 本地文件
  802. * @param remoteFile 远程文件
  803. * @param timeout 超时时间,单位秒
  804. * @return true
  805. * @return false
  806. */
  807. bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remoteFile, size_t timeout, bool isCreateDir)
  808. {
  809. if(m_ftpUrl.empty())
  810. {
  811. SPDLOG_ERROR("Url is empty");
  812. return false;
  813. }
  814. /* 检查本地文件是否存在 */
  815. if(!std::filesystem::exists(localFile))
  816. {
  817. SPDLOG_ERROR("Local file is not exist: {}", localFile);
  818. return false;
  819. }
  820. /* 检查FTP文件名是否符合规范 */
  821. std::string remoteFileTmp = checkFilePath(remoteFile);
  822. std::string remoteDirTmp = remoteFileTmp.substr(0, remoteFileTmp.find_last_of("/"));
  823. /* 检查远程FTP上的文件夹是否存在,不存在则创建 */
  824. if(!isDirExist(remoteDirTmp))
  825. {
  826. if(isCreateDir)
  827. {
  828. if(!createDirectories(remoteDirTmp))
  829. {
  830. // SPDLOG_ERROR("Failed to create remote dir: {}", remoteFileTmp);
  831. return false;
  832. }
  833. }else {
  834. SPDLOG_ERROR("Remote dir is not exist: {}", remoteDirTmp);
  835. return false;
  836. }
  837. }
  838. /* 拼接远程文件的url */
  839. std::string ftpUrl = m_ftpUrl + remoteFileTmp;
  840. /* 打开文件 */
  841. std::ifstream ifs;
  842. ifs.open(localFile, std::ios::in | std::ios::binary);
  843. if(!ifs.is_open())
  844. {
  845. SPDLOG_ERROR("Failed to open local file: {}", localFile);
  846. return false;
  847. }
  848. /* 获取文件大小 */
  849. // auto startPos = ifs.tellg();
  850. ifs.seekg(0, std::ios::end);
  851. auto fileSize = ifs.tellg();
  852. /* 恢复指针到文件头 */
  853. ifs.seekg(0, std::ios::beg);
  854. SPDLOG_DEBUG("File size: {}", (long)fileSize);
  855. // CURL* curl = nullptr;
  856. // curl = curl_easy_init();
  857. // if(curl == nullptr)
  858. // {
  859. // SPDLOG_ERROR("curl init failed !");
  860. // ifs.close();
  861. // return false;
  862. // }
  863. if(!resetCurl(m_curl))
  864. {
  865. ifs.close();
  866. return false;
  867. }
  868. /* 设置FTP地址 */
  869. curl_easy_setopt(m_curl, CURLOPT_URL, ftpUrl.c_str());
  870. curl_easy_setopt(m_curl, CURLOPT_PORT, m_port);
  871. /* 设置用户名和密码 */
  872. curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
  873. curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
  874. /* 启用跟随重定向 */
  875. curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
  876. /* 启用上传 */
  877. curl_easy_setopt(m_curl, CURLOPT_UPLOAD, 1L);
  878. /* 设置回调函数 */
  879. curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, readFileCallBack);
  880. curl_easy_setopt(m_curl, CURLOPT_READDATA, &ifs);
  881. /* 设置上传文件的大小 */
  882. curl_easy_setopt(m_curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fileSize);
  883. /* 设置超时时间 */
  884. // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
  885. curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, timeout);
  886. /* 设置进度回调函数 */
  887. curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
  888. curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, nullptr);
  889. /* 启用下载进度回调函数 */
  890. curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
  891. /* 设置SFTP */
  892. setSftp(m_curl);
  893. /* 启用调试信息 */
  894. if(m_enableCurlDebug)
  895. {
  896. curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
  897. }
  898. /* 启用持久连接 */
  899. curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
  900. /* 发送请求 */
  901. bool ret = performCurl(m_curl);
  902. if(!ret)
  903. {
  904. SPDLOG_ERROR("Upload file failed, Url = {}", ftpUrl);
  905. }
  906. /* 关闭文件,清理curl */
  907. ifs.close();
  908. // curl_easy_cleanup(curl);
  909. /* 打印一个换行符 */
  910. printf("\n");
  911. return ret;
  912. }
  913. /**
  914. * @brief 上传文件,上传数据
  915. * 注意:函数内不会拷贝数据,因此在上传完成前需要保证该指针的有效性,拷贝完成后也不会销毁源数据
  916. * 默认超时时间是30秒,也无法设置
  917. *
  918. * @param srcData 数据指针
  919. * @param size 数据大小
  920. * @param remoteFile 远程文件名,包括地址
  921. * @param timeout 超时时间,单位秒
  922. * @return true
  923. * @return false
  924. */
  925. bool CurlFtp::uploadData(char* srcData, size_t size, const std::string& remoteFile, size_t timeout, bool isCreateDir)
  926. {
  927. if(m_ftpUrl.empty())
  928. {
  929. SPDLOG_ERROR("Url is empty");
  930. return false;
  931. }
  932. /* 初始化本地数据 */
  933. CF_ArrayInfo arrayInfo;
  934. arrayInfo.data = srcData;
  935. arrayInfo.size = size;
  936. arrayInfo.pos = 0;
  937. /* 检查FTP文件名是否符合规范 */
  938. std::string remoteFileTmp = checkFilePath(remoteFile);
  939. std::string remoteDirTmp = remoteFileTmp.substr(0, remoteFileTmp.find_last_of("/"));
  940. /* 检查远程FTP上的文件夹是否存在,不存在则创建 */
  941. if(!isDirExist(remoteDirTmp))
  942. {
  943. if(isCreateDir)
  944. {
  945. if(!createDirectories(remoteDirTmp))
  946. {
  947. // SPDLOG_ERROR("Failed to create remote dir: {}", remoteFileTmp);
  948. return false;
  949. }
  950. }else {
  951. SPDLOG_ERROR("Remote dir is not exist: {}", remoteDirTmp);
  952. return false;
  953. }
  954. }
  955. /* 拼接远程文件的url */
  956. std::string ftpUrl = m_ftpUrl + remoteFileTmp;
  957. SPDLOG_DEBUG("Data size: {}", arrayInfo.size);
  958. // CURL* curl = nullptr;
  959. // curl = curl_easy_init();
  960. // if(curl == nullptr)
  961. // {
  962. // SPDLOG_ERROR("curl init failed !");
  963. // return false;
  964. // }
  965. if(!resetCurl(m_curl))
  966. {
  967. return false;
  968. }
  969. /* 设置FTP地址 */
  970. curl_easy_setopt(m_curl, CURLOPT_URL, ftpUrl.c_str());
  971. curl_easy_setopt(m_curl, CURLOPT_PORT, m_port);
  972. /* 设置用户名和密码 */
  973. // curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
  974. // curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
  975. curl_easy_setopt(m_curl, CURLOPT_USERPWD, (m_username + ":" + m_password).c_str());
  976. /* 启用跟随重定向 */
  977. curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
  978. /* 启用上传 */
  979. curl_easy_setopt(m_curl, CURLOPT_UPLOAD, 1L);
  980. /* 设置回调函数 */
  981. curl_easy_setopt(m_curl, CURLOPT_READFUNCTION, readDataCallBack);
  982. curl_easy_setopt(m_curl, CURLOPT_READDATA, &arrayInfo);
  983. /* 设置上传文件的大小 */
  984. curl_easy_setopt(m_curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)arrayInfo.size);
  985. /* 设置超时时间 */
  986. // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
  987. curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, timeout);
  988. /* 设置进度回调函数 */
  989. curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
  990. curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, nullptr);
  991. /* 启用下载进度回调函数 */
  992. curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
  993. // 禁用被动模式(如果需要)
  994. curl_easy_setopt(m_curl, CURLOPT_FTP_USE_EPSV, 1L);
  995. if(m_enableCurlDebug)
  996. {
  997. /* 启用调试信息 */
  998. curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
  999. }
  1000. /* 启用持久连接 */
  1001. curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
  1002. /* 发送请求 */
  1003. bool ret = performCurl(m_curl);
  1004. if(!ret)
  1005. {
  1006. SPDLOG_ERROR("Upload file failed, Url = {}", ftpUrl);
  1007. }
  1008. /* 关闭文件,清理curl */
  1009. // curl_easy_cleanup(curl);
  1010. /* 打印一个换行符 */
  1011. printf("\n");
  1012. return ret;
  1013. }
  1014. /**
  1015. * @brief 列出文件列表,这个需要的参数很多,无法设置成静态函数
  1016. *
  1017. * @param curl CURL句柄
  1018. * @param dir 文件夹,相对路径,不带有IP和端口号
  1019. * @param fileList 返回值,文件列表
  1020. * @return true
  1021. * @return false
  1022. */
  1023. bool CurlFtp::listAll(CURL* curl, std::string dir, std::vector<CF_FileInfo>& fileInfoList)
  1024. {
  1025. if(m_IP.empty())
  1026. {
  1027. SPDLOG_ERROR("IP is empty");
  1028. return false;
  1029. }
  1030. bool result = false;
  1031. std::string strSrc;
  1032. /* 先设置FTP地址 */
  1033. std::string ftpUrl = m_ftpUrl + dir;
  1034. curl_easy_setopt(curl, CURLOPT_URL, ftpUrl.c_str());
  1035. curl_easy_setopt(curl, CURLOPT_PORT, m_port);
  1036. /* 设置用户名和密码 */
  1037. curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
  1038. curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
  1039. /* 设置列出文件命令,只列出文件名称,不携带信息 */
  1040. // curl_easy_setopt(m_curl, CURLOPT_DIRLISTONLY, 1L);
  1041. /* 设置回调函数 */
  1042. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback);
  1043. /* 设置需要写入的容器,回调函数的第四个参数 */
  1044. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strSrc);
  1045. /* 设置超时时间 */
  1046. curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
  1047. curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
  1048. // 禁用被动模式,设置为0是禁用(如果需要)
  1049. // curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
  1050. if(m_enableCurlDebug)
  1051. {
  1052. /* 启用调试信息 */
  1053. curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
  1054. }
  1055. /* 发送请求 */
  1056. // CURLcode res = curl_easy_perform(curl);
  1057. bool ret = performCurl(curl);
  1058. if(!ret)
  1059. {
  1060. SPDLOG_ERROR("Failed to get file listUrl = {}", ftpUrl);
  1061. return false;
  1062. }
  1063. /* 解析字符串 */
  1064. parseFileInfo(strSrc, fileInfoList);
  1065. return true;
  1066. }
  1067. /* 检查文件夹路径是否合规,不合规就修改 */
  1068. std::string CurlFtp::checkDirPath(const std::string& dir)
  1069. {
  1070. std::string dirTmp = dir;
  1071. /* 检查是否以“/”开头 */
  1072. std::regex reg(R"(^/[.]*)");
  1073. if(!std::regex_match(dirTmp, reg))
  1074. {
  1075. dirTmp = "/" + dirTmp;
  1076. }
  1077. /* 如果有重复的“/”,替换成一个 “/” */
  1078. std::regex reg1(R"(//)");
  1079. dirTmp = std::regex_replace(dirTmp, reg1, "/");
  1080. /* 检查是否以“/”结束 */
  1081. std::regex reg2(R"([.]*/$)");
  1082. if(!std::regex_match(dirTmp, reg2))
  1083. {
  1084. dirTmp = dirTmp + "/";
  1085. }
  1086. return dirTmp;
  1087. }
  1088. /* 检查文件路径是否合规 */
  1089. std::string CurlFtp::checkFilePath(const std::string& file)
  1090. {
  1091. std::string dirTmp = file;
  1092. /* 检查是否以“/”结束,如果是以“/”结尾,返回空字符串,并报错 */
  1093. std::regex reg2(R"([.]*/$)");
  1094. if(std::regex_match(dirTmp, reg2))
  1095. {
  1096. SPDLOG_ERROR("File path is not correct, end with '/'");
  1097. return std::string();
  1098. }
  1099. /* 检查是否以“/”开头 */
  1100. std::regex reg(R"(^/[.]*)");
  1101. if(!std::regex_match(dirTmp, reg))
  1102. {
  1103. dirTmp = "/" + dirTmp;
  1104. }
  1105. /* 如果有重复的“/”,替换成一个 “/” */
  1106. std::regex reg1(R"(//)");
  1107. dirTmp = std::regex_replace(dirTmp, reg1, "/");
  1108. return dirTmp;
  1109. }
  1110. /**
  1111. * @brief 检查本地文件夹是否存在,不存在则创建
  1112. * 这里默认传入的是文件夹路径,不是文件路径,如果最后有‘/’,会自动去掉
  1113. *
  1114. * @param localDir 文件夹路径
  1115. * @return true
  1116. * @return false
  1117. */
  1118. bool CurlFtp::checkLocalDir(const std::string& localDir)
  1119. {
  1120. /* 去掉最后的‘/’ */
  1121. std::regex reg(R"([.]*/$)");
  1122. std::string localDirTmp = std::regex_replace(localDir, reg, "");
  1123. /* 检查文件夹是否存在 */
  1124. if(std::filesystem::exists(localDirTmp))
  1125. {
  1126. return true;
  1127. }
  1128. /* 创建文件夹 */
  1129. if(!std::filesystem::create_directories(localDirTmp))
  1130. {
  1131. SPDLOG_ERROR("Failed to create local dir: {}", localDirTmp);
  1132. return false;
  1133. }
  1134. return true;
  1135. }
  1136. /* 执行curl,添加重试机制 */
  1137. bool CurlFtp::performCurl(CURL* curl)
  1138. {
  1139. int retry = 3;
  1140. while(retry > 0)
  1141. {
  1142. CURLcode res = curl_easy_perform(curl);
  1143. if(res == CURLE_OK)
  1144. {
  1145. return true;
  1146. }
  1147. else if (res == CURLE_LOGIN_DENIED)
  1148. {
  1149. SPDLOG_ERROR("Login failed, error code: {} ,{}", (int)res, curl_easy_strerror(res));
  1150. /* 设置用户名和密码 */
  1151. curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
  1152. curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
  1153. }
  1154. else
  1155. {
  1156. SPDLOG_ERROR("Perform curl failed, error code: {} ,{}", (int)res, curl_easy_strerror(res));
  1157. SPDLOG_ERROR("Retry times: {}", 4 - retry);
  1158. }
  1159. retry--;
  1160. }
  1161. return false;
  1162. }
  1163. /* 重置curl设置 */
  1164. bool CurlFtp::resetCurl(CURL* curl)
  1165. {
  1166. if(m_curl == nullptr)
  1167. {
  1168. m_curl = curl_easy_init();
  1169. if(m_curl == nullptr)
  1170. {
  1171. SPDLOG_ERROR("curl init failed !");
  1172. return false;
  1173. }
  1174. }else {
  1175. curl_easy_reset(m_curl);
  1176. }
  1177. return true;
  1178. }
  1179. /* 设置sftp,如果是sftp就设置 */
  1180. bool CurlFtp::setSftp(CURL* curl)
  1181. {
  1182. /* 判断是否是SFTP */
  1183. if(m_isSftp)
  1184. {
  1185. /* 判断是否需要设置CA证书 */
  1186. if(m_isIgnoreSSLCert)
  1187. {
  1188. /* 忽略证书验证 */
  1189. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  1190. /* 忽略主机名验证 */
  1191. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  1192. } else
  1193. {
  1194. /* 设置CA证书 */
  1195. curl_easy_setopt(curl, CURLOPT_CAINFO, m_caCertFile.c_str());
  1196. }
  1197. }
  1198. return true;
  1199. }