You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

525 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "curl_utils.h"
#include <chrono>
// 回调函数,用于处理从服务器接收到的数据
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<HeaderData*>(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<string> split_regex(const string& str, const string& pattern) {
vector<string> 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");
}
}