|
|
@@ -347,6 +347,54 @@ static int progress_callback(void *clientp,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* ======================== 通配符列表示例所需回调 ======================== */
|
|
|
+/* 分块开始回调:收集文件信息并跳过实际传输 */
|
|
|
+static long chunk_bgn_cb(const void* transfer_info, void* ptr, int /*remains*/)
|
|
|
+{
|
|
|
+ const struct curl_fileinfo* finfo = static_cast<const struct curl_fileinfo*>(transfer_info);
|
|
|
+ if(finfo == nullptr || ptr == nullptr)
|
|
|
+ {
|
|
|
+ return CURL_CHUNK_BGN_FUNC_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::vector<CF_FileInfo>* out = static_cast<std::vector<CF_FileInfo>*>(ptr);
|
|
|
+
|
|
|
+ // 跳过 "." 和 ".." 等无效名称
|
|
|
+ if(finfo->filename && (std::string(finfo->filename) == "." || std::string(finfo->filename) == ".."))
|
|
|
+ {
|
|
|
+ return CURL_CHUNK_BGN_FUNC_SKIP;
|
|
|
+ }
|
|
|
+
|
|
|
+ CF_FileInfo fi;
|
|
|
+ if(finfo->filetype == CURLFILETYPE_DIRECTORY)
|
|
|
+ {
|
|
|
+ fi.type = CF_FileType::DIR;
|
|
|
+ }
|
|
|
+ else if(finfo->filetype == CURLFILETYPE_FILE)
|
|
|
+ {
|
|
|
+ fi.type = CF_FileType::FILE;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // 其他类型(链接、设备等)不处理
|
|
|
+ return CURL_CHUNK_BGN_FUNC_SKIP;
|
|
|
+ }
|
|
|
+
|
|
|
+ fi.size = static_cast<uint64_t>(finfo->size);
|
|
|
+ fi.name = (finfo->filename ? std::string(finfo->filename) : std::string());
|
|
|
+
|
|
|
+ out->push_back(std::move(fi));
|
|
|
+
|
|
|
+ // 仅做枚举,不实际下载数据
|
|
|
+ return CURL_CHUNK_BGN_FUNC_SKIP;
|
|
|
+}
|
|
|
+
|
|
|
+/* 分块结束回调:无特别处理 */
|
|
|
+static long chunk_end_cb(void* /*ptr*/)
|
|
|
+{
|
|
|
+ return CURL_CHUNK_END_FUNC_OK;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
/* 使用Windows API进行编码转换 */
|
|
|
#if defined(_WIN32)
|
|
|
@@ -389,7 +437,7 @@ const std::regex parseReg4(R"(^-.*$)");
|
|
|
*/
|
|
|
static bool parseFileInfo(const std::string& strSrc, std::vector<CF_FileInfo>& fileInfo)
|
|
|
{
|
|
|
- SPDLOG_INFO("SRC FILE INFO:\n{}", strSrc);
|
|
|
+ SPDLOG_DEBUG("SRC FILE INFO:\n{}", strSrc);
|
|
|
#if defined(_WIN32)
|
|
|
// auto str1 = GBToUTF8(strSrc.c_str());
|
|
|
std::string str2 = strSrc;
|
|
|
@@ -445,6 +493,30 @@ static bool parseFileInfo(const std::string& strSrc, std::vector<CF_FileInfo>& f
|
|
|
}
|
|
|
|
|
|
|
|
|
+/* 辅助:拆分绝对路径为 父目录(以/结尾) + 基名 */
|
|
|
+static void splitParentAndBasePath(const std::string& absPath, std::string& parentDir, std::string& fileName)
|
|
|
+{
|
|
|
+ auto pos = absPath.find_last_of('/');
|
|
|
+ if(pos == std::string::npos)
|
|
|
+ {
|
|
|
+ parentDir = "/";
|
|
|
+ fileName = absPath;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ parentDir = absPath.substr(0, pos + 1);
|
|
|
+ fileName = absPath.substr(pos + 1);
|
|
|
+ if(parentDir.empty()) parentDir = "/";
|
|
|
+}
|
|
|
+
|
|
|
+/* 去掉最后的“/” */
|
|
|
+std::string trimTrailingSlash(std::string s)
|
|
|
+{
|
|
|
+ if (!s.empty() && s.back() == '/')
|
|
|
+ s.pop_back();
|
|
|
+ return s;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/* ==================================================================================
|
|
|
* *********************************** 成员函数 *************************************
|
|
|
* ================================================================================== */
|
|
|
@@ -520,6 +592,25 @@ void CurlFtp::enableCurlDebug(bool isPrint)
|
|
|
}
|
|
|
|
|
|
|
|
|
+/* 获取列表,包括文件和文件夹 */
|
|
|
+bool CurlFtp::getList(std::string dir, std::vector<CF_FileInfo>& fileInfoList)
|
|
|
+{
|
|
|
+ if(m_IP.empty())
|
|
|
+ {
|
|
|
+ LOG_WARN("IP or port is empty");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ resetCurl(m_curl);
|
|
|
+
|
|
|
+ /* 获取文件信息 */
|
|
|
+ listByWildcard(m_curl, dir, fileInfoList);
|
|
|
+
|
|
|
+ // curl_easy_cleanup(curl);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
/* 列出文件列表 */
|
|
|
bool CurlFtp::getFileList(std::string dir, std::vector<std::string>& fileList)
|
|
|
{
|
|
|
@@ -533,7 +624,7 @@ bool CurlFtp::getFileList(std::string dir, std::vector<std::string>& fileList)
|
|
|
|
|
|
std::vector<CF_FileInfo> listInfo;
|
|
|
/* 获取文件信息 */
|
|
|
- listAll(m_curl, dirTmp, listInfo);
|
|
|
+ listByWildcard(m_curl, dirTmp, listInfo);
|
|
|
for(const CF_FileInfo& fi : listInfo)
|
|
|
{
|
|
|
// LOG_INFO("type = {}, size = {}, name = {}", (int)fi.type, fi.size, fi.name);
|
|
|
@@ -570,7 +661,7 @@ bool CurlFtp::getDirList(std::string dir, std::vector<std::string>& dirList)
|
|
|
|
|
|
std::vector<CF_FileInfo> listInfo;
|
|
|
/* 获取文件信息 */
|
|
|
- listAll(m_curl, dirTmp, listInfo);
|
|
|
+ listByWildcard(m_curl, dirTmp, listInfo);
|
|
|
for(const CF_FileInfo& fi : listInfo)
|
|
|
{
|
|
|
// LOG_INFO("type = {}, size = {}, name = {}", (int)fi.type, fi.size, fi.name);
|
|
|
@@ -594,7 +685,7 @@ bool CurlFtp::getDirList(std::string dir, std::vector<std::string>& dirList)
|
|
|
* @return true
|
|
|
* @return false
|
|
|
*/
|
|
|
-bool CurlFtp::isDirExist(const std::string& dir)
|
|
|
+bool CurlFtp::existsDir(const std::string& dir)
|
|
|
{
|
|
|
LOG_DEBUG("Check remote dir:" << dir);
|
|
|
if(m_ftpUrl.empty())
|
|
|
@@ -606,99 +697,14 @@ bool CurlFtp::isDirExist(const std::string& dir)
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
- bool result = false;
|
|
|
- if(m_isSftp)
|
|
|
- {
|
|
|
- result = checkSftpDirExist(dir);
|
|
|
- }else {
|
|
|
- result = checkFtpDirExist(dir);
|
|
|
- }
|
|
|
|
|
|
- return result;
|
|
|
+ return checkFtpDirExist(dir);
|
|
|
}
|
|
|
|
|
|
|
|
|
-/**
|
|
|
- * @brief 创建FTP文件夹,只能创建一层文件夹
|
|
|
- *
|
|
|
- * @param ftpDir 文件夹路径
|
|
|
- * @return true 文件夹创建成功或者文件夹存在
|
|
|
- * @return false
|
|
|
- */
|
|
|
-bool CurlFtp::createDirectory(const std::string& ftpDir)
|
|
|
-{
|
|
|
- if(m_ftpUrl.empty())
|
|
|
- {
|
|
|
- LOG_ERROR("ftpUrl is empty");
|
|
|
- return false;
|
|
|
- }
|
|
|
- /* 先检查FTP文件夹是否存在,如果存在直接返回true */
|
|
|
- if(isDirExist(ftpDir))
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- /* 检查传入的文件夹格式 */
|
|
|
- auto dirTmp = checkDirPath(ftpDir);
|
|
|
-
|
|
|
- // CURL *curl = curl_easy_init();
|
|
|
- // if(curl == nullptr)
|
|
|
- // {
|
|
|
- // LOG_ERROR("Create FTP DIR, curl init failed !");
|
|
|
- // return false;
|
|
|
- // }
|
|
|
- // resetCurl(m_curl);
|
|
|
- setCommonCurlOptions(m_curl, m_ftpUrl, 30);
|
|
|
-
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_URL, m_ftpUrl.c_str());
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
|
|
|
- /* 创建FTP头信息 */
|
|
|
- struct curl_slist *headerlist = NULL;
|
|
|
- std::string mkdir;
|
|
|
- if(m_isSftp)
|
|
|
- {
|
|
|
- mkdir = "MKDIR " + dirTmp;
|
|
|
- }else {
|
|
|
- mkdir = "MKD " + dirTmp;
|
|
|
- }
|
|
|
- headerlist = curl_slist_append(headerlist, mkdir.c_str());
|
|
|
- /* 设置FTP命令行选项 */
|
|
|
- curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
|
|
|
- /* 不包含实体 */
|
|
|
- curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
- /* 启用跟随重定向 */
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
|
- /* 设置超时时间 */
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, 30L);
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, 30L);
|
|
|
-
|
|
|
- /* 设置SFTP */
|
|
|
- // setSftp(m_curl);
|
|
|
-
|
|
|
- // if(m_enableCurlDebug)
|
|
|
- // {
|
|
|
- // /* 启用调试信息 */
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
|
|
|
- // }
|
|
|
- // 启用持久连接
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
|
|
|
-
|
|
|
- LOG_DEBUG("Create remote dir: " << dirTmp);
|
|
|
- // CURLcode res = curl_easy_perform(curl);
|
|
|
- bool ret = performCurl(m_curl);
|
|
|
- if(!ret)
|
|
|
- {
|
|
|
- LOG_ERROR("Failed to create remote Dir");
|
|
|
- }
|
|
|
-
|
|
|
- // curl_easy_cleanup(curl);
|
|
|
- curl_slist_free_all(headerlist);
|
|
|
- return ret;
|
|
|
-}
|
|
|
|
|
|
/* 创建FTP文件夹,递归创建 */
|
|
|
-bool CurlFtp::createDirectories(const std::string& ftpDir)
|
|
|
+bool CurlFtp::createDirectory(const std::string& ftpDir)
|
|
|
{
|
|
|
/* 检查路径格式,并去掉第一个 / */
|
|
|
std::string ftpDirTmp = checkDirPath(ftpDir);
|
|
|
@@ -728,7 +734,7 @@ bool CurlFtp::createDirectories(const std::string& ftpDir)
|
|
|
for(const std::string& dir : dirList)
|
|
|
{
|
|
|
// LOG_DEBUG("Create dir: {}", dir);
|
|
|
- if(!createDirectory(dir))
|
|
|
+ if(!createOneDirectory(dir))
|
|
|
{
|
|
|
LOG_ERROR("Failed to create dir: " << dir);
|
|
|
return false;
|
|
|
@@ -741,7 +747,10 @@ bool CurlFtp::createDirectories(const std::string& ftpDir)
|
|
|
|
|
|
|
|
|
|
|
|
-/* 删除文件 */
|
|
|
+
|
|
|
+
|
|
|
+/* 删除文件,文件后面不能带有“/”否则会报错
|
|
|
+ 删除方式,先 CWD 到该文件目录,再执行 DELE 删除命令 */
|
|
|
bool CurlFtp::deleteFile(const std::string& remoteFile)
|
|
|
{
|
|
|
if(m_ftpUrl.empty())
|
|
|
@@ -749,12 +758,78 @@ bool CurlFtp::deleteFile(const std::string& remoteFile)
|
|
|
LOG_ERROR("ftpUrl is empty");
|
|
|
return false;
|
|
|
}
|
|
|
+ /* 规范化文件路径(以/开头,且不以/结尾) */
|
|
|
+ std::string filePath = checkFilePath(remoteFile);
|
|
|
+ if(filePath.empty())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
+ /* 切到父目录后再删除基名(兼容更多服务器) */
|
|
|
+ std::string parentDir, fileName;
|
|
|
+ splitParentAndBasePath(filePath, parentDir, fileName);
|
|
|
+ if(fileName.empty())
|
|
|
+ {
|
|
|
+ LOG_ERROR("Delete file base name is empty: " << filePath);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- return true;
|
|
|
+ std::string parentUrl = m_ftpUrl + parentDir; // parentDir 以'/'结尾
|
|
|
+ if(!setCommonCurlOptions(m_curl, parentUrl, 10))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct curl_slist *headerlist = nullptr;
|
|
|
+ std::string deleteCmd;
|
|
|
+ if(m_isSftp)
|
|
|
+ {
|
|
|
+ deleteCmd = "rm " + fileName; // sftp 使用 rm
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ deleteCmd = "DELE " + fileName; // ftp 使用 DELE
|
|
|
+ }
|
|
|
+ headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+ LOG_DEBUG("Delete remote file: " << filePath << ", parent: " << parentDir);
|
|
|
+ bool ret = performCurl(m_curl);
|
|
|
+ curl_slist_free_all(headerlist);
|
|
|
+ if(ret)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 回退:尝试使用绝对路径删除(有些服务器允许) */
|
|
|
+ LOG_WARN("Delete by basename failed, try absolute path: " << filePath);
|
|
|
+ std::string rootUrl = m_ftpUrl + "/";
|
|
|
+ if(!setCommonCurlOptions(m_curl, rootUrl, 10))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ headerlist = nullptr;
|
|
|
+ if(m_isSftp)
|
|
|
+ {
|
|
|
+ deleteCmd = "rm " + filePath;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ deleteCmd = "DELE " + filePath;
|
|
|
+ }
|
|
|
+ headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+ bool ret2 = performCurl(m_curl);
|
|
|
+ curl_slist_free_all(headerlist);
|
|
|
+ if(!ret2)
|
|
|
+ {
|
|
|
+ LOG_ERROR("Failed to delete remote file: " << filePath);
|
|
|
+ }
|
|
|
+ return ret2;
|
|
|
}
|
|
|
|
|
|
-/* 删除文件夹 */
|
|
|
+/* 删除文件夹,递归删除 */
|
|
|
bool CurlFtp::deleteDirectory(const std::string& ftpDir)
|
|
|
{
|
|
|
if(m_ftpUrl.empty())
|
|
|
@@ -762,8 +837,120 @@ bool CurlFtp::deleteDirectory(const std::string& ftpDir)
|
|
|
LOG_ERROR("ftpUrl is empty");
|
|
|
return false;
|
|
|
}
|
|
|
+ /* 规范化目录路径,并去掉末尾斜杠 */
|
|
|
+ std::string dirWithSlash = checkDirPath(ftpDir);
|
|
|
+ std::string dirNoSlash = trimTrailingSlash(dirWithSlash);
|
|
|
+ if(dirNoSlash.empty() || dirNoSlash == "/")
|
|
|
+ {
|
|
|
+ LOG_ERROR("Refuse to delete root dir: " << dirNoSlash);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- return true;
|
|
|
+ /* 递归删除:先删除内部所有文件和子目录,再删除自身 */
|
|
|
+ std::vector<CF_FileInfo> vecChildren;
|
|
|
+ // 使用通配符列举该目录下的所有条目
|
|
|
+ if(!listByWildcard(m_curl, dirWithSlash, vecChildren))
|
|
|
+ {
|
|
|
+ LOG_WARN("List children failed before delete: " << dirWithSlash);
|
|
|
+ }
|
|
|
+ // 先删文件, 先切换到该目录,避免某些服务器删除失败
|
|
|
+ if(!changeToDir(dirWithSlash))
|
|
|
+ {
|
|
|
+ LOG_ERROR("ChangeToDir failed: " << dirWithSlash);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for(const auto& fi : vecChildren)
|
|
|
+ {
|
|
|
+ /* 软连接这些也用文件删除,不过目前貌似无法获取到软连接 */
|
|
|
+ if(fi.type != CF_FileType::DIR)
|
|
|
+ {
|
|
|
+ if(fi.name.empty() || fi.name == "." || fi.name == "..")
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(!deleteFileRelative(fi.name))
|
|
|
+ {
|
|
|
+ LOG_ERROR("Failed to delete file: " << fi.name);
|
|
|
+ // 回退:尝试使用绝对路径删除
|
|
|
+ std::string absFile = dirWithSlash + fi.name;
|
|
|
+ if(!deleteFile(absFile))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 再递归删子目录
|
|
|
+ for(const auto& fi : vecChildren)
|
|
|
+ {
|
|
|
+ if(fi.type == CF_FileType::DIR)
|
|
|
+ {
|
|
|
+ if(fi.name.empty() || fi.name == "." || fi.name == "..")
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ std::string childDir = dirWithSlash + fi.name;
|
|
|
+ std::string childNorm = checkDirPath(childDir);
|
|
|
+ if(childNorm == dirWithSlash)
|
|
|
+ {
|
|
|
+ LOG_WARN("Skip self-recursion: " << childNorm);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(!deleteDirectory(childNorm))
|
|
|
+ {
|
|
|
+ LOG_ERROR("Failed to delete subdir: " << childDir);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 删除自身目录,走到这一步,这个文件夹内的所有文件都被删除了 */
|
|
|
+ std::string parentDir, baseName;
|
|
|
+ splitParentAndBasePath(dirNoSlash, parentDir, baseName);
|
|
|
+ if(baseName.empty())
|
|
|
+ {
|
|
|
+ LOG_ERROR("Delete directory base name is empty: " << dirNoSlash);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ /* 切换到父文件夹并删除当前文件夹 */
|
|
|
+ if(!changeToDir(parentDir))
|
|
|
+ {
|
|
|
+ LOG_ERROR("ChangeToDir parent failed: " << parentDir);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if(deleteDirectoryRelative(baseName))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 绝对路径删除回退(部分服务器支持) */
|
|
|
+ {
|
|
|
+ std::string rootUrl = m_ftpUrl + "/";
|
|
|
+ if(!setCommonCurlOptions(m_curl, rootUrl, 10))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ struct curl_slist *headerlist = nullptr;
|
|
|
+ std::string deleteCmd;
|
|
|
+ if(m_isSftp)
|
|
|
+ {
|
|
|
+ deleteCmd = std::string("rmdir ") + dirNoSlash;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ deleteCmd = std::string("RMD ") + dirNoSlash;
|
|
|
+ }
|
|
|
+ headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+ bool ret2 = performCurl(m_curl);
|
|
|
+ curl_slist_free_all(headerlist);
|
|
|
+ if(!ret2)
|
|
|
+ {
|
|
|
+ LOG_ERROR("Failed to delete remote directory: " << dirNoSlash);
|
|
|
+ }
|
|
|
+ return ret2;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -926,11 +1113,11 @@ bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remote
|
|
|
std::string remoteDirTmp = remoteFileTmp.substr(0, remoteFileTmp.find_last_of("/"));
|
|
|
// printf("uploadFile: %d\n", __LINE__);
|
|
|
/* 检查远程FTP上的文件夹是否存在,不存在则创建 */
|
|
|
- if(!isDirExist(remoteDirTmp))
|
|
|
+ if(!existsDir(remoteDirTmp))
|
|
|
{
|
|
|
if(isCreateDir)
|
|
|
{
|
|
|
- if(!createDirectories(remoteDirTmp))
|
|
|
+ if(!createDirectory(remoteDirTmp))
|
|
|
{
|
|
|
// LOG_ERROR("Failed to create remote dir: {}", remoteFileTmp);
|
|
|
return false;
|
|
|
@@ -1030,11 +1217,11 @@ bool CurlFtp::uploadData(char* srcData, size_t size, const std::string& remoteFi
|
|
|
std::string remoteFileTmp = checkFilePath(remoteFile);
|
|
|
std::string remoteDirTmp = remoteFileTmp.substr(0, remoteFileTmp.find_last_of("/"));
|
|
|
/* 检查远程FTP上的文件夹是否存在,不存在则创建 */
|
|
|
- if(!isDirExist(remoteDirTmp))
|
|
|
+ if(!existsDir(remoteDirTmp))
|
|
|
{
|
|
|
if(isCreateDir)
|
|
|
{
|
|
|
- if(!createDirectories(remoteDirTmp))
|
|
|
+ if(!createDirectory(remoteDirTmp))
|
|
|
{
|
|
|
// LOG_ERROR("Failed to create remote dir: {}", remoteFileTmp);
|
|
|
return false;
|
|
|
@@ -1101,7 +1288,59 @@ bool CurlFtp::uploadData(char* srcData, size_t size, const std::string& remoteFi
|
|
|
* @return true
|
|
|
* @return false
|
|
|
*/
|
|
|
-bool CurlFtp::listAll(CURL* curl, std::string dir, std::vector<CF_FileInfo>& fileInfoList)
|
|
|
+// bool CurlFtp::listAll(CURL* curl, std::string dir, std::vector<CF_FileInfo>& fileInfoList)
|
|
|
+// {
|
|
|
+// if(m_IP.empty())
|
|
|
+// {
|
|
|
+// LOG_ERROR("IP is empty");
|
|
|
+// return false;
|
|
|
+// }
|
|
|
+
|
|
|
+// // bool result = false;
|
|
|
+// std::string strSrc;
|
|
|
+// /* 先设置FTP地址 */
|
|
|
+// std::string ftpUrl = m_ftpUrl + dir;
|
|
|
+// if(!setCommonCurlOptions(curl, ftpUrl, 10))
|
|
|
+// {
|
|
|
+// return false;
|
|
|
+// }
|
|
|
+
|
|
|
+// /* 设置列出文件命令,只列出文件名称,不携带信息 */
|
|
|
+// // curl_easy_setopt(m_curl, CURLOPT_DIRLISTONLY, 1L);
|
|
|
+// /* 设置为文本传输 */
|
|
|
+// curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, 1L);
|
|
|
+// /* 设置回调函数 */
|
|
|
+// curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback);
|
|
|
+// /* 设置需要写入的容器,回调函数的第四个参数 */
|
|
|
+// curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strSrc);
|
|
|
+
|
|
|
+// // 禁用被动模式,设置为0是禁用(如果需要)
|
|
|
+// // curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
|
|
|
+// /* 发送请求 */
|
|
|
+// // CURLcode res = curl_easy_perform(curl);
|
|
|
+// bool ret = performCurl(curl);
|
|
|
+// if(!ret)
|
|
|
+// {
|
|
|
+// LOG_ERROR("Failed to get file listUrl = " << ftpUrl);
|
|
|
+// return false;
|
|
|
+// }
|
|
|
+// /* 解析字符串 */
|
|
|
+// parseFileInfo(strSrc, fileInfoList);
|
|
|
+
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 列出文件列表,这个需要的参数很多,无法设置成静态函数
|
|
|
+ *
|
|
|
+ * @param curl CURL句柄
|
|
|
+ * @param dir 文件夹,相对路径,不带有IP和端口号
|
|
|
+ * @param fileList 返回值,文件列表
|
|
|
+ * @return true
|
|
|
+ * @return false
|
|
|
+ */
|
|
|
+bool CurlFtp::listByWildcard(CURL* curl, const std::string& dir, std::vector<CF_FileInfo>& out)
|
|
|
{
|
|
|
if(m_IP.empty())
|
|
|
{
|
|
|
@@ -1109,36 +1348,33 @@ bool CurlFtp::listAll(CURL* curl, std::string dir, std::vector<CF_FileInfo>& fil
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- // bool result = false;
|
|
|
- std::string strSrc;
|
|
|
- /* 先设置FTP地址 */
|
|
|
- std::string ftpUrl = m_ftpUrl + dir;
|
|
|
- if(!setCommonCurlOptions(curl, m_ftpUrl + dir, 10))
|
|
|
+ /* 规范化目录并追加通配符 */
|
|
|
+ std::string dirWithSlash = checkDirPath(dir); // 保证以'/'结束
|
|
|
+ std::string ftpUrl = m_ftpUrl + dirWithSlash + "*";
|
|
|
+
|
|
|
+ if(!setCommonCurlOptions(curl, ftpUrl, 10))
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- /* 设置列出文件命令,只列出文件名称,不携带信息 */
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_DIRLISTONLY, 1L);
|
|
|
- /* 设置回调函数 */
|
|
|
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback);
|
|
|
- /* 设置需要写入的容器,回调函数的第四个参数 */
|
|
|
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strSrc);
|
|
|
+ /* 启用通配符匹配并注册回调,仅收集信息不下载 */
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_WILDCARDMATCH, 1L);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn_cb);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end_cb);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_CHUNK_DATA, &out);
|
|
|
|
|
|
- // 禁用被动模式,设置为0是禁用(如果需要)
|
|
|
- // curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 1L);
|
|
|
- /* 发送请求 */
|
|
|
- // CURLcode res = curl_easy_perform(curl);
|
|
|
- bool ret = performCurl(curl);
|
|
|
- if(!ret)
|
|
|
+ // FTP 文本模式对目录兼容性更好(SFTP 忽略该设置)
|
|
|
+ if(m_isSftp == false)
|
|
|
{
|
|
|
- LOG_ERROR("Failed to get file listUrl = " << ftpUrl);
|
|
|
- return false;
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_TRANSFERTEXT, 1L);
|
|
|
}
|
|
|
- /* 解析字符串 */
|
|
|
- parseFileInfo(strSrc, fileInfoList);
|
|
|
|
|
|
- return true;
|
|
|
+ bool ret = performCurl(m_curl);
|
|
|
+ if(!ret)
|
|
|
+ {
|
|
|
+ LOG_ERROR("Wildcard list failed, Url = " << ftpUrl);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1192,10 +1428,9 @@ std::string CurlFtp::checkDirPath(const std::string& dir)
|
|
|
std::regex reg1(R"(//)");
|
|
|
dirTmp = std::regex_replace(dirTmp, reg1, "/");
|
|
|
/* 检查是否以“/”结束 */
|
|
|
- std::regex reg2(R"([.]*/$)");
|
|
|
- if(!std::regex_match(dirTmp, reg2))
|
|
|
+ if (dirTmp.back() != '/')
|
|
|
{
|
|
|
- dirTmp = dirTmp + "/";
|
|
|
+ dirTmp.push_back('/');
|
|
|
}
|
|
|
|
|
|
return dirTmp;
|
|
|
@@ -1284,14 +1519,14 @@ bool CurlFtp::checkLocalDirExist(const std::string& localDir)
|
|
|
bool CurlFtp::checkLocalFileExist(const std::string& localFile)
|
|
|
{
|
|
|
/* 去掉最后的‘/’ */
|
|
|
- std::regex reg(R"([.]*/$)");
|
|
|
- std::string localDirTmp = std::regex_replace(localFile, reg, "");
|
|
|
+ // std::regex reg(R"([.]*/$)");
|
|
|
+ std::string localDirTmp = trimTrailingSlash(localFile);
|
|
|
/* 检查文件是否存在 */
|
|
|
bool result = false;
|
|
|
// #if (__cplusplus >= 201703L)
|
|
|
-// result = std::filesystem::exists(localFile);
|
|
|
+// result = std::filesystem::exists(localDirTmp);
|
|
|
// #else
|
|
|
- result = QFile(localFile.c_str()).exists();
|
|
|
+ result = QFile(localDirTmp.c_str()).exists();
|
|
|
// #endif /* (__cplusplus >= 201703L) */
|
|
|
|
|
|
return result;
|
|
|
@@ -1316,7 +1551,11 @@ bool CurlFtp::performCurl(CURL* curl)
|
|
|
curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
|
|
|
}
|
|
|
- else
|
|
|
+ else if(res == CURLE_REMOTE_FILE_NOT_FOUND)
|
|
|
+ {
|
|
|
+ LOG_WARN("Remote file or directory not found, error code: " << (int)res << ", " << curl_easy_strerror(res));
|
|
|
+ return true;
|
|
|
+ } else
|
|
|
{
|
|
|
LOG_ERROR("Perform curl failed, error code: " << (int)res << ", " << curl_easy_strerror(res));
|
|
|
LOG_ERROR("Retry times: " << (4 - retry));
|
|
|
@@ -1368,6 +1607,7 @@ bool CurlFtp::setSftp(CURL* curl)
|
|
|
|
|
|
/**
|
|
|
* @brief 检查FTP文件夹是否存在,注意,传入的文件夹最后一定要带/,否则会检查的是文件
|
|
|
+ * 现在检测方式和SFTP一致,都使用列出上一层目录的方式进行检测
|
|
|
*
|
|
|
* @param dir
|
|
|
* @return true
|
|
|
@@ -1377,69 +1617,111 @@ bool CurlFtp::checkFtpDirExist(const std::string& dir)
|
|
|
{
|
|
|
/* 检查传入的文件夹 */
|
|
|
auto dirTmp = checkDirPath(dir);
|
|
|
- if(!setCommonCurlOptions(m_curl, m_ftpUrl + dir, 10))
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
- // resetCurl(m_curl);
|
|
|
- // std::string ftpUrl = m_ftpUrl + dirTmp;
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_URL, ftpUrl.c_str());
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
|
|
|
- /* 获取文件夹是否存在,不需要接收文件 */
|
|
|
- curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_HEADER, 1L);
|
|
|
+ // dirTmp = m_ftpUrl + dirTmp;
|
|
|
+ // if(!setCommonCurlOptions(m_curl, dirTmp, 10))
|
|
|
+ // {
|
|
|
+ // return false;
|
|
|
+ // }
|
|
|
+// /* 获取文件夹是否存在,不需要接收文件 */
|
|
|
+// curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+// // curl_easy_setopt(m_curl, CURLOPT_HEADER, 1L);
|
|
|
+
|
|
|
+// /* 启用持久连接 */
|
|
|
+// // curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
|
|
|
+// /* 这里只需要执行一次 */
|
|
|
+// CURLcode res = curl_easy_perform(m_curl);
|
|
|
+// bool result = true;
|
|
|
+// if(res == CURLE_REMOTE_FILE_NOT_FOUND)
|
|
|
+// {
|
|
|
+// result = false;
|
|
|
+// }
|
|
|
+// else if(res == CURLE_OK)
|
|
|
+// {
|
|
|
+// result = true;
|
|
|
+// } else
|
|
|
+// {
|
|
|
+// LOG_ERROR("Check remote dir error, error code: " << (int)res << ", " << curl_easy_strerror(res));
|
|
|
+// result = false;
|
|
|
+// }
|
|
|
+// // LOG_DEBUG("Check remote dir: {}, res: {}", dir, (int)res);
|
|
|
|
|
|
- /* 启用持久连接 */
|
|
|
- // curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
|
|
|
- /* 这里只需要执行一次 */
|
|
|
- CURLcode res = curl_easy_perform(m_curl);
|
|
|
- bool result = true;
|
|
|
- if(res == CURLE_REMOTE_FILE_NOT_FOUND)
|
|
|
+ /* 获取文件夹 */
|
|
|
+ std::string parentDir = QFileInfo(QString::fromStdString(dir)).path().toStdString();
|
|
|
+ std::vector<CF_FileInfo> vecDir;
|
|
|
+ bool ret = listByWildcard(m_curl, parentDir, vecDir);
|
|
|
+ if(!ret)
|
|
|
{
|
|
|
- result = false;
|
|
|
+ LOG_ERROR("Failed to check ftp dir: " << dir);
|
|
|
+ return false;
|
|
|
}
|
|
|
- else if(res == CURLE_OK)
|
|
|
- {
|
|
|
- result = true;
|
|
|
- } else
|
|
|
+ /* 取出本层文件夹名称 */
|
|
|
+ std::string dirName = QFileInfo(QString::fromStdString(dir)).fileName().toStdString();
|
|
|
+ /* 判断是否存在 */
|
|
|
+ bool result = false;
|
|
|
+ for(const auto& fi : vecDir)
|
|
|
{
|
|
|
- LOG_ERROR("Check remote dir error, error code: " << (int)res << ", " << curl_easy_strerror(res));
|
|
|
- result = false;
|
|
|
+ if(fi.name == dirName && fi.type == CF_FileType::DIR)
|
|
|
+ {
|
|
|
+ result = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- // LOG_DEBUG("Check remote dir: {}, res: {}", dir, (int)res);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/* 检查SFTP文件夹是否存在,这里通过列出上一层的文件夹中的内容,然后进行对比判断的 */
|
|
|
-bool CurlFtp::checkSftpDirExist(const std::string& dir)
|
|
|
+// bool CurlFtp::checkSftpDirExist(const std::string& dir)
|
|
|
+// {
|
|
|
+// /* 取出上一层文件夹路径 */
|
|
|
+// std::string parentDir = QFileInfo(QString::fromStdString(dir)).path().toStdString();
|
|
|
+// std::vector<std::string> vecDir;
|
|
|
+// bool ret = getDirList(parentDir, vecDir);
|
|
|
+// if(!ret)
|
|
|
+// {
|
|
|
+// LOG_ERROR("Failed to check sftp dir: " << dir);
|
|
|
+// return false;
|
|
|
+// }
|
|
|
+// /* 取出本层文件夹名称 */
|
|
|
+// std::string dirName = QFileInfo(QString::fromStdString(dir)).fileName().toStdString();
|
|
|
+// /* 判断是否存在 */
|
|
|
+// bool result = false;
|
|
|
+// for(const std::string& str : vecDir)
|
|
|
+// {
|
|
|
+// if(str == dirName)
|
|
|
+// {
|
|
|
+// result = true;
|
|
|
+// break;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// return result;
|
|
|
+// }
|
|
|
+
|
|
|
+/* 检查FTP/SFTP文件是否存在,使用通配符获取文件列表匹配 */
|
|
|
+bool CurlFtp::checkFtpFileExist(const std::string& file)
|
|
|
{
|
|
|
+ /* 判断文件字符是否符合规范 */
|
|
|
+ auto fileTmp = checkFilePath(file);
|
|
|
+ if(fileTmp.empty())
|
|
|
+ {
|
|
|
+ LOG_ERROR("File path is not correct: " << file);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
/* 取出上一层文件夹路径 */
|
|
|
-// #if (__cplusplus >= 201703L)
|
|
|
-// std::string parentDir = std::filesystem::path(dir).parent_path().string();
|
|
|
-// #else
|
|
|
- std::string parentDir = QFileInfo(QString::fromStdString(dir)).path().toStdString();
|
|
|
-// #endif /* (__cplusplus >= 201703L) */
|
|
|
- // LOG_DEBUG("Parent dir: {}", parentDir);
|
|
|
- std::vector<std::string> vecDir;
|
|
|
- bool ret = getDirList(parentDir, vecDir);
|
|
|
+ std::string parentDir = QFileInfo(QString::fromStdString(file)).path().toStdString();
|
|
|
+ std::vector<CF_FileInfo> vecFiles;
|
|
|
+ bool ret = listByWildcard(m_curl, parentDir, vecFiles);
|
|
|
if(!ret)
|
|
|
{
|
|
|
- LOG_ERROR("Failed to check sftp dir: " << dir);
|
|
|
+ LOG_ERROR("Failed to check ftp file: " << file);
|
|
|
return false;
|
|
|
}
|
|
|
- /* 取出本层文件夹名称 */
|
|
|
-// #if (__cplusplus >= 201703L)
|
|
|
-// std::string dirName = std::filesystem::path(dir).filename().string();
|
|
|
-// #else
|
|
|
- std::string dirName = QFileInfo(QString::fromStdString(dir)).fileName().toStdString();
|
|
|
-// #endif /* (__cplusplus >= 201703L) */
|
|
|
- // LOG_DEBUG("Dir name: {}", dirName);
|
|
|
+ /* 取出本层文件名称 */
|
|
|
+ std::string fileName = QFileInfo(QString::fromStdString(file)).fileName().toStdString();
|
|
|
/* 判断是否存在 */
|
|
|
bool result = false;
|
|
|
- for(const std::string& str : vecDir)
|
|
|
+ for(const auto& fi : vecFiles)
|
|
|
{
|
|
|
- if(str == dirName)
|
|
|
+ if(fi.name == fileName && fi.type != CF_FileType::DIR)
|
|
|
{
|
|
|
result = true;
|
|
|
break;
|
|
|
@@ -1449,4 +1731,159 @@ bool CurlFtp::checkSftpDirExist(const std::string& dir)
|
|
|
}
|
|
|
|
|
|
|
|
|
+/* 进入一个路径 */
|
|
|
+bool CurlFtp::changeToDir(const std::string& dir)
|
|
|
+{
|
|
|
+ if(m_ftpUrl.empty())
|
|
|
+ {
|
|
|
+ LOG_ERROR("ftpUrl is empty");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 规范化目录,确保以'/'结尾
|
|
|
+ std::string dirWithSlash = checkDirPath(dir);
|
|
|
+ std::string url = m_ftpUrl + dirWithSlash;
|
|
|
+
|
|
|
+ // 设置到该目录并发送一个不传输实体的请求,相当于验证可进入
|
|
|
+ if(!setCommonCurlOptions(m_curl, url, 10))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+ LOG_DEBUG("Change to remote dir: " << dirWithSlash);
|
|
|
+ return performCurl(m_curl);
|
|
|
+}
|
|
|
+
|
|
|
+/* 删除一个文件,相对路径,调用这个函数,需要先调用上面那个函数进入路径 */
|
|
|
+bool CurlFtp::deleteFileRelative(const std::string& remoteFile)
|
|
|
+{
|
|
|
+ // 相对删除:要求已通过 changeToDir() 进入父目录
|
|
|
+ // 仅接受基名,不允许包含'/',且不允许以'/'结尾
|
|
|
+ if(remoteFile.empty())
|
|
|
+ {
|
|
|
+ LOG_ERROR("Delete relative file name is empty");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 不允许包含路径分隔符
|
|
|
+ if(remoteFile.find('/') != std::string::npos)
|
|
|
+ {
|
|
|
+ LOG_ERROR("DeleteFileRelative only accepts basename without '/' : " << remoteFile);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 当前 m_curl 已经被 changeToDir() 设置到父目录 URL
|
|
|
+ // 直接下发 DELE/rm 命令
|
|
|
+ struct curl_slist *headerlist = nullptr;
|
|
|
+ std::string deleteCmd;
|
|
|
+ if(m_isSftp)
|
|
|
+ {
|
|
|
+ deleteCmd = "rm " + remoteFile; // sftp 使用 rm
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ deleteCmd = "DELE " + remoteFile; // ftp 使用 DELE
|
|
|
+ }
|
|
|
+ headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+ LOG_DEBUG("Delete relative file: " << remoteFile);
|
|
|
+ bool ret = performCurl(m_curl);
|
|
|
+ curl_slist_free_all(headerlist);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/* 删除一个文件夹,相对路径,调用这个函数,需要先调用上面那个函数进入路径 */
|
|
|
+bool CurlFtp::deleteDirectoryRelative(const std::string& ftpDir)
|
|
|
+{
|
|
|
+ // 相对删除:要求已通过 changeToDir() 进入父目录
|
|
|
+ // 仅接受基名,不允许包含'/',且不允许以'/'结尾
|
|
|
+ if(ftpDir.empty())
|
|
|
+ {
|
|
|
+ LOG_ERROR("Delete relative dir name is empty");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 不允许包含路径分隔符
|
|
|
+ if(ftpDir.find('/') != std::string::npos)
|
|
|
+ {
|
|
|
+ LOG_ERROR("DeleteDirectoryRelative only accepts basename without '/' : " << ftpDir);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 当前 m_curl 已经被 changeToDir() 设置到父目录 URL
|
|
|
+ // 直接下发 RMD/rmdir 命令
|
|
|
+ struct curl_slist *headerlist = nullptr;
|
|
|
+ std::string deleteCmd;
|
|
|
+ if(m_isSftp)
|
|
|
+ {
|
|
|
+ deleteCmd = "rmdir " + ftpDir; // sftp 使用 rmdir
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ deleteCmd = "RMD " + ftpDir; // ftp 使用 RMD
|
|
|
+ }
|
|
|
+ headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+ LOG_DEBUG("Delete relative directory: " << ftpDir);
|
|
|
+ bool ret = performCurl(m_curl);
|
|
|
+ curl_slist_free_all(headerlist);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 创建FTP文件夹,只能创建一层文件夹
|
|
|
+ *
|
|
|
+ * @param ftpDir 文件夹路径
|
|
|
+ * @return true 文件夹创建成功或者文件夹存在
|
|
|
+ * @return false
|
|
|
+ */
|
|
|
+bool CurlFtp::createOneDirectory(const std::string& ftpDir)
|
|
|
+{
|
|
|
+ if(m_ftpUrl.empty())
|
|
|
+ {
|
|
|
+ LOG_ERROR("ftpUrl is empty");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ /* 先检查FTP文件夹是否存在,如果存在直接返回true */
|
|
|
+ if(existsDir(ftpDir))
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ setCommonCurlOptions(m_curl, m_ftpUrl, 30);
|
|
|
+
|
|
|
+ /* 检查传入的文件夹格式 */
|
|
|
+ auto dirTmp = checkDirPath(ftpDir);
|
|
|
+ /* 去掉末尾的“/” */
|
|
|
+ auto dirNoSlash = trimTrailingSlash(dirTmp);
|
|
|
+
|
|
|
+ /* 创建FTP头信息 */
|
|
|
+ struct curl_slist *headerlist = NULL;
|
|
|
+ std::string mkdir;
|
|
|
+ if(m_isSftp)
|
|
|
+ {
|
|
|
+ mkdir = "mkdir " + dirNoSlash;
|
|
|
+ }else {
|
|
|
+ mkdir = "MKD " + dirNoSlash;
|
|
|
+ }
|
|
|
+ headerlist = curl_slist_append(headerlist, mkdir.c_str());
|
|
|
+ /* 设置FTP命令行选项 */
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
|
|
|
+ /* 不包含实体 */
|
|
|
+ curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
|
|
|
+
|
|
|
+ LOG_DEBUG("Create remote dir: " << dirTmp);
|
|
|
+ // CURLcode res = curl_easy_perform(curl);
|
|
|
+ bool ret = performCurl(m_curl);
|
|
|
+ if(!ret)
|
|
|
+ {
|
|
|
+ LOG_ERROR("Failed to create remote Dir");
|
|
|
+ }
|
|
|
+
|
|
|
+ // curl_easy_cleanup(curl);
|
|
|
+ curl_slist_free_all(headerlist);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
|