// #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include #include // Linux/macOS 下的 mkdir 函数 #include // 补充 Linux 下的文件状态定义 #include "minio_download.h" #include "thread_local_logger.h" // 辅助函数:将二进制数据转换为十六进制字符串 std::string bin_to_hex(const unsigned char* data, size_t len) { std::stringstream ss; ss << std::hex << std::setfill('0'); for (size_t i = 0; i < len; ++i) { ss << std::setw(2) << static_cast(data[i]); } return ss.str(); } // 计算SHA256哈希并返回十六进制字符串 std::string sha256_hex(const std::string& data) { unsigned char digest[SHA256_DIGEST_LENGTH]; SHA256((const unsigned char*)data.c_str(), data.size(), digest); return bin_to_hex(digest, SHA256_DIGEST_LENGTH); } // 计算HMAC-SHA256并返回二进制数据 std::string hmac_sha256(const std::string& key, const std::string& data) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_len; HMAC(EVP_sha256(), key.c_str(), key.size(), (unsigned char*)data.c_str(), data.size(), digest, &digest_len); return std::string((char*)digest, digest_len); } // 获取当前时间的ISO8601格式 (YYYYMMDD'T'HHMMSS'Z') std::string get_iso8601_time() { std::time_t now = std::time(nullptr); std::tm tm = *std::gmtime(&now); // 使用GMT时间 std::stringstream ss; ss << std::put_time(&tm, "%Y%m%dT%H%M%SZ"); return ss.str(); } // 回调函数:将下载的数据写入文件 size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) { size_t total_size = size * nmemb; std::ofstream* file = static_cast(userp); file->write(static_cast(contents), total_size); return total_size; } // 从MinIO下载文件 bool download_from_minio(const std::string& endpoint, const std::string& access_key, const std::string& secret_key, const std::string& bucket, const std::string& object_name, const std::string& output_file) { CURL* curl = curl_easy_init(); if (!curl) { LOG_ERROR("初始化curl失败"); return false; } // 构建请求URL std::string url = endpoint + "/" + bucket + "/" + object_name; // 准备认证信息 std::string timestamp = get_iso8601_time(); std::string date = timestamp.substr(0, 8); // 提取日期部分 (YYYYMMDD) std::string region = "us-east-1"; // MinIO默认区域,如使用其他区域需修改 std::string service = "s3"; std::string request_type = "aws4_request"; // 提取主机名(去除http://或https://) size_t host_start = endpoint.find("://"); if (host_start == std::string::npos) { host_start = 0; } else { host_start += 3; } std::string host = endpoint.substr(host_start); // 构建规范请求 (Canonical Request) // 格式:HTTPMethod + "\n" + CanonicalURI + "\n" + CanonicalQueryString + "\n" + // CanonicalHeaders + "\n" + SignedHeaders + "\n" + PayloadHash std::string canonical_uri = "/" + bucket + "/" + object_name; std::string canonical_query_string = ""; // GET请求无查询参数 std::string canonical_headers = "host:" + host + "\n" + "x-amz-date:" + timestamp + "\n"; std::string signed_headers = "host;x-amz-date"; // 必须与上面的headers顺序一致 std::string payload_hash = sha256_hex(""); // 空请求体的哈希 std::string canonical_request = "GET\n" + canonical_uri + "\n" + canonical_query_string + "\n" + canonical_headers + "\n" + signed_headers + "\n" + payload_hash; // 构建待签名字符串 (String to Sign) std::string credential_scope = date + "/" + region + "/" + service + "/" + request_type; std::string string_to_sign = "AWS4-HMAC-SHA256\n" + timestamp + "\n" + credential_scope + "\n" + sha256_hex(canonical_request); // 计算签名密钥 (Signing Key) std::string k_date = hmac_sha256("AWS4" + secret_key, date); std::string k_region = hmac_sha256(k_date, region); std::string k_service = hmac_sha256(k_region, service); std::string k_signing = hmac_sha256(k_service, request_type); // 计算最终签名 std::string signature = bin_to_hex( (const unsigned char*)hmac_sha256(k_signing, string_to_sign).c_str(), SHA256_DIGEST_LENGTH ); // 构建Authorization头 std::string auth_header = "Authorization: AWS4-HMAC-SHA256 " "Credential=" + access_key + "/" + credential_scope + ", " "SignedHeaders=" + signed_headers + ", " "Signature=" + signature; // 打开输出文件 std::ofstream outfile(output_file, std::ios::binary); if (!outfile.is_open()) { LOG_ERROR("无法打开输出文件: " ,output_file); curl_easy_cleanup(curl); return false; } // 设置curl选项 curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &outfile); // 设置请求头 struct curl_slist* headers = nullptr; headers = curl_slist_append(headers, ("Host: " + host).c_str()); headers = curl_slist_append(headers, ("x-amz-date: " + timestamp).c_str()); headers = curl_slist_append(headers, auth_header.c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 开启调试模式(调试时使用) curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); // SSL设置(生产环境建议开启验证) #ifdef NDEBUG curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); #else // 调试环境可以关闭SSL验证 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); #endif // 执行请求 CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { LOG_ERROR("curl请求失败: " ,curl_easy_strerror(res)); long response_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); LOG_ERROR("响应代码: " ,response_code); // 输出调试信息帮助排查签名问题 LOG_ERROR("调试信息:"); LOG_ERROR("URL: " ,url); LOG_ERROR("Canonical Request:" ,canonical_request); LOG_ERROR("String to Sign:",string_to_sign); LOG_ERROR("Signature:" ,signature); } // 清理资源 curl_slist_free_all(headers); curl_easy_cleanup(curl); outfile.close(); if (res == CURLE_OK) { LOG_INFO( "文件下载成功: " ,output_file ); return true; } return false; } /* int main() { // 初始化libcurl curl_global_init(CURL_GLOBAL_DEFAULT); // MinIO配置 - 根据实际情况修改 std::string endpoint = "http://111.32.12.11:9000"; // 替换为你的MinIO地址 std::string access_key = "UDM59PO2GGFR6AM8LSXP"; // 替换为你的Access Key std::string secret_key = "YShs5u5aj5Znc7kZkfEkel1QagG9eKko+C0mRUHr"; // 替换为你的Secret Key std::string bucket = "2025-09-16"; // 替换为你的存储桶名称 std::string object_name = "19/output/321.pcap_fj.jsonl"; // 替换为要下载的对象名称 std::string output_file = "321.pcap_fj.jsonl"; // 保存的文件名 // 下载文件 bool success = download_from_minio(endpoint, access_key, secret_key, bucket, object_name, output_file); // 清理libcurl curl_global_cleanup(); return success ? 0 : 1; }*/