#include "curl_utils.h" #include // 回调函数,用于处理从服务器接收到的数据 size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) { size_t totalSize = size * nmemb; userp->append((char*)contents, totalSize); return totalSize; } // 回调函数,用于处理接收到的响应头 size_t HeaderCallback(char* buffer, size_t size, size_t nitems, void* userdata) { HeaderData* headerData = static_cast(userdata); std::string header(buffer, size * nitems); if (!headerData->found && header.find("x-csrf-token:") == 0) { size_t colonPos = header.find(':'); if (colonPos != std::string::npos) { headerData->value = header.substr(colonPos + 1); headerData->value.erase(0, headerData->value.find_first_not_of(" \t")); headerData->value.erase(headerData->value.length() - 2); headerData->found = true; // 标记为已找到 } } return size * nitems; } vector split_regex(const string& str, const string& pattern) { vector tokens; regex rgx(pattern); sregex_token_iterator iter(str.begin(), str.end(), rgx, -1); sregex_token_iterator end; while (iter != end) { tokens.push_back(*iter++); } return tokens; } string URLEncodeFTPPath2(const std::string& path) { CURL* curl = curl_easy_init(); if (!curl) return path; std::string result; std::string encoded_part; // 分割路径,分别编码每个部分 size_t start = 0; size_t end = path.find('/'); while (end != std::string::npos) { std::string part = path.substr(start, end - start); if (!part.empty()) { char* encoded = curl_easy_escape(curl, part.c_str(), part.length()); if (encoded) { result += encoded; curl_free(encoded); } else { result += part; } } result += '/'; start = end + 1; end = path.find('/', start); } // 最后一部分 if (start < path.length()) { std::string part = path.substr(start); char* encoded = curl_easy_escape(curl, part.c_str(), part.length()); if (encoded) { result += encoded; curl_free(encoded); } else { result += part; } } curl_easy_cleanup(curl); return result; } // 写入回调函数(用于上传文件) size_t read_callback(void* ptr, size_t size, size_t nmemb, FILE* stream) { size_t retcode = fread(ptr, size, nmemb, stream); return retcode; } int sendFileByFtp(string ftp_url, string file_path, string file_dir, string user, string pwd) { int result = 0; CURL* curl; CURLcode res; // 初始化libcurl curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { string encoded_ftp_url = URLEncodeFTPPath2(ftp_url); // 1. 创建远程目录 curl_easy_setopt(curl, CURLOPT_URL, encoded_ftp_url.c_str()); curl_easy_setopt(curl, CURLOPT_USERNAME, user.c_str()); curl_easy_setopt(curl, CURLOPT_PASSWORD, pwd.c_str()); curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR); // 重要:对于非标准端口,可能需要额外设置 curl_easy_setopt(curl, CURLOPT_FTPPORT, "-"); // 被动模式 curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 0L); // 禁用 EPSV // 启用详细日志查看具体问题 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // 设置超时 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); // 执行目录创建 res = curl_easy_perform(curl); if (res != CURLE_OK) { WriteLog("FTP 创建目录失: %s (错误: %d)\n", curl_easy_strerror(res), res); result = 1; goto end; } // 2. 上传文件到新目录 FILE* file = nullptr; errno_t err = fopen_s(&file, file_path.c_str(), "rb"); if (err != 0 || file == nullptr) { // 处理错误 WriteLog("无法打开本地文件>%s\n", file_path.c_str()); result = 1; goto end; } //FILE* file = fopen(file_path.c_str(), "rb"); //if (!file) { // WriteLog("无法打开本地文件>%s\n", file_path.c_str()); // result = 1; // goto end; //} // 设置FTP上传选项 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_URL, file_dir.c_str()); curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); curl_easy_setopt(curl, CURLOPT_READDATA, file); // 获取文件大小 fseek(file, 0, SEEK_END); curl_off_t file_size = ftell(file); fseek(file, 0, SEEK_SET); curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, file_size); // 执行文件上传 res = curl_easy_perform(curl); if (res != CURLE_OK) { WriteLog("上传文件失败:%s\n", curl_easy_strerror(res)); result = 1; } else { WriteLog("文件上传成功\n"); } fclose(file); curl_easy_cleanup(curl); } end: curl_global_cleanup(); return result; } string postJSON(string url, string body, long& http_code, string& error) { string response_data = ""; CURL* curl; CURLcode res; // 初始化 libcurl 会话 curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { // 设置要访问的 URL curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "http"); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // 设置请求头 struct curl_slist* headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json; charset=UTF-8"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size()); // 设置回调函数以接收响应数据 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data); // 执行请求 res = curl_easy_perform(curl); // 检查错误 if (res != CURLE_OK) { error = curl_easy_strerror(res); } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // 清理资源 curl_slist_free_all(headers); curl_easy_cleanup(curl); } // 清理全局环境 curl_global_cleanup(); return response_data; } // URL 编码函数(处理空格等特殊字符) std::string URLEncodeFTPPath(const std::string& path) { CURL* curl = curl_easy_init(); if (!curl) return path; std::string result; std::string encoded_part; // 分割路径,分别编码每个部分 size_t start = 0; size_t end = path.find('/'); while (end != std::string::npos) { std::string part = path.substr(start, end - start); if (!part.empty()) { char* encoded = curl_easy_escape(curl, part.c_str(), part.length()); if (encoded) { result += encoded; curl_free(encoded); } else { result += part; } } result += '/'; start = end + 1; end = path.find('/', start); } // 最后一部分 if (start < path.length()) { std::string part = path.substr(start); char* encoded = curl_easy_escape(curl, part.c_str(), part.length()); if (encoded) { result += encoded; curl_free(encoded); } else { result += part; } } curl_easy_cleanup(curl); return result; } bool TestBasicFTPConnection() { CURL* curl = curl_easy_init(); if (!curl) return false; // 最简单的测试 - 只连接到根目录 std::string test_url = "ftp://192.168.120.92:22/"; WriteLog("测试基础连接: %s\n", test_url.c_str()); curl_easy_setopt(curl, CURLOPT_URL, test_url.c_str()); curl_easy_setopt(curl, CURLOPT_USERNAME, "ftp1"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "Jd@tc00"); // 需要密码 // 只连接,不执行任何操作 curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); // 启用详细日志 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { WriteLog("基础连接失败: %s (错误: %d)\n", curl_easy_strerror(res), res); } else { WriteLog("基础连接成功\n"); } curl_easy_cleanup(curl); return (res == CURLE_OK); } bool UploadToFTPWithDebug() { // 你的参数 std::string server = "192.168.120.92"; int port = 22; std::string username = "ftp1"; std::string password = "Jd@tc00"; std::string remote_dir = "DocumentRevision/MS WordX/WD00000644-10"; std::string filename = "WD00000644_ceshiwen.docx"; std::string local_file = "C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\2\\WD00000644_ceshiwen.docx"; WriteLog("=== FTP 上传调试开始 ===\n"); WriteLog("服务器: %s:%d\n", server.c_str(), port); WriteLog("用户名: %s\n", username.c_str()); WriteLog("本地文件: %s\n", local_file.c_str()); WriteLog("远程目录: %s\n", remote_dir.c_str()); WriteLog("文件名: %s\n", filename.c_str()); // 1. 先测试基础连接 if (!TestBasicFTPConnection()) { WriteLog("基础连接测试失败,请检查网络和凭据\n"); return false; } // 2. 创建目录 CURL* curl_dir = curl_easy_init(); if (curl_dir) { // 构建目录URL - 特别注意空格的处理 std::string dir_path = remote_dir; // 方法1:直接使用路径(libcurl 会自动处理空格) std::string dir_url = "ftp://" + server + ":" + std::to_string(port) + "/" + dir_path + "/"; WriteLog("尝试创建目录,URL: %s\n", dir_url.c_str()); curl_easy_setopt(curl_dir, CURLOPT_URL, dir_url.c_str()); curl_easy_setopt(curl_dir, CURLOPT_USERNAME, username.c_str()); curl_easy_setopt(curl_dir, CURLOPT_PASSWORD, password.c_str()); curl_easy_setopt(curl_dir, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR); // 重要:设置 FTP 选项 curl_easy_setopt(curl_dir, CURLOPT_FTPPORT, "-"); // 被动模式 curl_easy_setopt(curl_dir, CURLOPT_FTP_USE_EPSV, 0L); // 禁用 EPSV // 设置超时 curl_easy_setopt(curl_dir, CURLOPT_TIMEOUT, 30L); curl_easy_setopt(curl_dir, CURLOPT_CONNECTTIMEOUT, 10L); // 启用详细日志 curl_easy_setopt(curl_dir, CURLOPT_VERBOSE, 1L); CURLcode res = curl_easy_perform(curl_dir); if (res != CURLE_OK) { WriteLog("目录创建失败: %s (错误: %d)\n", curl_easy_strerror(res), res); // 尝试另一种方法:手动编码 std::string encoded_dir = URLEncodeFTPPath(dir_path); std::string dir_url2 = "ftp://" + server + ":" + std::to_string(port) + "/" + encoded_dir + "/"; WriteLog("尝试方法2,编码URL: %s\n", dir_url2.c_str()); // 重置并重试 curl_easy_reset(curl_dir); curl_easy_setopt(curl_dir, CURLOPT_URL, dir_url2.c_str()); curl_easy_setopt(curl_dir, CURLOPT_USERNAME, username.c_str()); curl_easy_setopt(curl_dir, CURLOPT_PASSWORD, password.c_str()); curl_easy_setopt(curl_dir, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR); curl_easy_setopt(curl_dir, CURLOPT_FTPPORT, "-"); curl_easy_setopt(curl_dir, CURLOPT_VERBOSE, 1L); res = curl_easy_perform(curl_dir); if (res != CURLE_OK) { WriteLog("方法2也失败: %s\n", curl_easy_strerror(res)); } } else { WriteLog("目录创建成功\n"); } curl_easy_cleanup(curl_dir); } // 3. 上传文件 CURL* curl_upload = curl_easy_init(); if (!curl_upload) { WriteLog("初始化上传CURL失败\n"); return false; } // 打开本地文件 FILE* file = nullptr; errno_t err = fopen_s(&file, local_file.c_str(), "rb"); if (err != 0 || file == nullptr) { // 处理错误 WriteLog("无法打开本地文件>%s\n", local_file.c_str()); curl_easy_cleanup(curl_upload); return false; } // 获取文件大小 fseek(file, 0, SEEK_END); long file_size = ftell(file); fseek(file, 0, SEEK_SET); WriteLog("文件大小: %ld bytes\n", file_size); // 构建上传URL std::string upload_path = remote_dir + "/" + filename; std::string upload_url = "ftp://" + server + ":" + std::to_string(port) + "/" + upload_path; WriteLog("上传URL: %s\n", upload_url.c_str()); // 设置上传选项 curl_easy_setopt(curl_upload, CURLOPT_URL, upload_url.c_str()); curl_easy_setopt(curl_upload, CURLOPT_USERNAME, username.c_str()); curl_easy_setopt(curl_upload, CURLOPT_PASSWORD, password.c_str()); curl_easy_setopt(curl_upload, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl_upload, CURLOPT_READDATA, file); curl_easy_setopt(curl_upload, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size); // FTP 特定选项 curl_easy_setopt(curl_upload, CURLOPT_FTPPORT, "-"); // 被动模式 curl_easy_setopt(curl_upload, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR); // 设置超时 curl_easy_setopt(curl_upload, CURLOPT_TIMEOUT, 60L); // 启用详细日志 curl_easy_setopt(curl_upload, CURLOPT_VERBOSE, 1L); // 执行上传 CURLcode res = curl_easy_perform(curl_upload); // 关闭文件 fclose(file); if (res != CURLE_OK) { WriteLog("上传失败: %s (错误: %d)\n", curl_easy_strerror(res), res); // 尝试使用编码的URL std::string encoded_upload_path = URLEncodeFTPPath(upload_path); std::string upload_url2 = "ftp://" + server + ":" + std::to_string(port) + "/" + encoded_upload_path; WriteLog("尝试编码URL上传: %s\n", upload_url2.c_str()); //file = fopen(local_file.c_str(), "rb"); // 重新打开文件 errno_t err = fopen_s(&file, local_file.c_str(), "rb"); if (err != 0 || file == nullptr) { // 处理错误 WriteLog("重新打开文件无法打开本地文件>%s\n", local_file.c_str()); curl_easy_cleanup(curl_upload); return false; } if (file) { curl_easy_reset(curl_upload); curl_easy_setopt(curl_upload, CURLOPT_URL, upload_url2.c_str()); curl_easy_setopt(curl_upload, CURLOPT_USERNAME, username.c_str()); curl_easy_setopt(curl_upload, CURLOPT_PASSWORD, password.c_str()); curl_easy_setopt(curl_upload, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl_upload, CURLOPT_READDATA, file); curl_easy_setopt(curl_upload, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size); curl_easy_setopt(curl_upload, CURLOPT_FTPPORT, "-"); curl_easy_setopt(curl_upload, CURLOPT_VERBOSE, 1L); res = curl_easy_perform(curl_upload); fclose(file); } } if (res == CURLE_OK) { WriteLog("=== 文件上传成功 ===\n"); WriteLog("文件已上传到: %s\n", upload_url.c_str()); } else { WriteLog("=== 文件上传失败 ===\n"); WriteLog("最终错误: %s\n", curl_easy_strerror(res)); } curl_easy_cleanup(curl_upload); return (res == CURLE_OK); } void CheckCurlFeatures() { curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW); WriteLog("=== libcurl 功能检查 ===\n"); WriteLog("libcurl 版本: %s\n", version_info->version); WriteLog("支持的协议:\n"); // 检查协议支持 if (version_info->protocols) { for (int i = 0; version_info->protocols[i]; i++) { WriteLog(" - %s\n", version_info->protocols[i]); } } // 检查特定协议 bool supports_ftp = false; bool supports_sftp = false; for (int i = 0; version_info->protocols[i]; i++) { if (strcmp(version_info->protocols[i], "ftp") == 0) { supports_ftp = true; } if (strcmp(version_info->protocols[i], "sftp") == 0) { supports_sftp = true; } } WriteLog("\nFTP 支持: %s\n", supports_ftp ? "是" : "否"); WriteLog("SFTP 支持: %s\n", supports_sftp ? "是" : "否"); if (!supports_ftp && !supports_sftp) { WriteLog("错误: libcurl 没有编译 FTP/SFTP 支持!\n"); WriteLog("需要重新编译 libcurl 或使用支持 FTP 的版本\n"); } }