当前位置: 首页 > news >正文

使用Curl进行本地MinIO的操作

前言

最近在做相关的项目中关于本地服务搭建和访问的技术验证,打进来最基本的数据访问,使用了C++。可以进行:服务器的可用性检查、Bucket的创建、文件夹的创建、文件的上传、文件的下载、文件夹和Bucket的存在性检查等基本接口,对自己做记录,同时希望能帮到有需要的朋友。法布施得智慧嘛!

环境

1.开发环境

vs2019

2.第三方库

第三方库
第三方库链接:
https://download.csdn.net/download/weixin_40523119/90713265

核心代码

  1. minio操作类
    下面展示一些 内联代码片
CurlMinIOApi.cpp
#include "CurlMinIOApi.h"
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/sha.h>#include "serverHelp.h"CurlMinIOApi::CurlMinIOApi(const std::string& m_publicKey): sysHost("192.168.99.32:9000") {isValid = false;if (m_publicKey.empty()|| sysHost.empty()) {return;}baseUrl = "http://" + sysHost + "/";auto result = Base64Utils::decodeAndSplit(m_publicKey, accessKey, secretKey);if (!result.first) {return;}isValid = true;  // 初始化成功curl_global_init(CURL_GLOBAL_DEFAULT);
}CurlMinIOApi::~CurlMinIOApi()
{curl_global_cleanup();
}std::string CurlMinIOApi::hmac_sha256(const std::string& key, const std::string& msg) {unsigned char* digest;digest = HMAC(EVP_sha256(), key.c_str(), key.length(), (unsigned char*)msg.c_str(), msg.length(), NULL, NULL);return std::string(reinterpret_cast<char*>(digest), SHA256_DIGEST_LENGTH);
}std::string CurlMinIOApi::sha256_hex(const std::string& data) {unsigned char hash[SHA256_DIGEST_LENGTH];SHA256((unsigned char*)data.c_str(), data.size(), hash);std::stringstream ss;for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];}return ss.str();
}std::string CurlMinIOApi::to_hex(const std::string& str) {std::stringstream ss;for (unsigned char c : str) {ss << std::hex << std::setw(2) << std::setfill('0') << (int)c;}return ss.str();
}std::string CurlMinIOApi::get_utc_time() {auto now = std::chrono::system_clock::now();std::time_t t_c = std::chrono::system_clock::to_time_t(now);std::tm tm;
#ifdef _WIN32gmtime_s(&tm, &t_c);
#elsegmtime_r(&t_c, &tm);
#endifstd::stringstream ss;ss << std::put_time(&tm, "%Y%m%dT%H%M%SZ");return ss.str();
}std::string CurlMinIOApi::get_utc_date() {auto now = std::chrono::system_clock::now();std::time_t t_c = std::chrono::system_clock::to_time_t(now);std::tm tm;
#ifdef _WIN32gmtime_s(&tm, &t_c);
#elsegmtime_r(&t_c, &tm);
#endifstd::stringstream ss;ss << std::put_time(&tm, "%Y%m%d");return ss.str();
}std::string CurlMinIOApi::buildAuthorization(const std::string& region, const std::string& service,const std::string& canonicalRequestHash,const std::string& utcTime, const std::string& utcDate) {std::string scope = utcDate + "/" + region + "/" + service + "/" + "aws4_request";std::string stringToSign = "AWS4-HMAC-SHA256\n" + utcTime + "\n" + scope + "\n" + canonicalRequestHash;std::string kDate = hmac_sha256("AWS4" + secretKey, utcDate);std::string kRegion = hmac_sha256(kDate, region);std::string kService = hmac_sha256(kRegion, service);std::string kSigning = hmac_sha256(kService, "aws4_request");std::string signature = to_hex(hmac_sha256(kSigning, stringToSign));std::string auth = "AWS4-HMAC-SHA256 Credential=" + accessKey + "/" + scope +", SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=" + signature;return auth;
}int CurlMinIOApi::checkMinIOServerHealth() {CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;// 选择 /minio/health/ready 健康检查接口std::string url = baseUrl + "minio/health/ready";std::string canonicalRequest ="GET\n""/minio/health/ready\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(region, service, canonicalRequestHash, utcTime, utcDate);headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L);curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);curl_easy_setopt(curl.get(), CURLOPT_CONNECTTIMEOUT, 5L); //10s timeoutCURLcode res = curl_easy_perform(curl.get());if (res != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}return static_cast<int>(CurlReturnCode::SUCCESS);}catch (const std::exception& ex) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}int CurlMinIOApi::createSite(const std::string& bucketName) {if ( bucketName.empty()) {return static_cast<int>(CurlReturnCode::ERROR_INVALID_INPUT);}// 全局初始化CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;std::string url = baseUrl + bucketName;std::string canonicalRequest ="PUT\n""/" + bucketName + "\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(/*accessKey, secretKey, */region, service,canonicalRequestHash, utcTime, utcDate);headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, "PUT");curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);CURLcode res = curl_easy_perform(curl.get());if (res != CURLE_OK) {logger.logMessage("createBucket: ERROR_CURL_PERFORM: " + std::string(curl_easy_strerror(res)));return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}long responseCode;curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &responseCode);// 成功情况if (responseCode == 200 || responseCode == 201|| responseCode == 409|| responseCode == 403) {return static_cast<int>(CurlReturnCode::SUCCESS);}// 其他情况统一认为错误return static_cast<int>(CurlReturnCode::ERROR_INVALID_RESPONSE);}catch (const std::exception& ex) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}int CurlMinIOApi::createFolderInSite(const std::string& bucketName,const std::string& folderName) {if (bucketName.empty() || folderName.empty()) {//logger.logMessage("createFolderInBucket: ERROR_INVALID_INPUT: Empty input parameters");return static_cast<int>(CurlReturnCode::ERROR_INVALID_INPUT);}CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {//logger.logMessage("createFolderInBucket: ERROR_CURL_INIT: curl_global_init failed");return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;std::string objectKey = folderName;if (objectKey.back() != '/') {objectKey += '/';  // 确保是以 / 结尾,表示文件夹}std::string url = baseUrl + bucketName + "/" + objectKey;std::string canonicalRequest ="PUT\n""/" + bucketName + "/" + objectKey + "\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(region, service,canonicalRequestHash, utcTime, utcDate);headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, "PUT");curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, 0L); // 设置 body 为 0字节CURLcode res = curl_easy_perform(curl.get());if (res != CURLE_OK) {//logger.logMessage("createFolderInBucket: ERROR_CURL_PERFORM: " + std::string(curl_easy_strerror(res)));return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}long responseCode;curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &responseCode);// 成功if (responseCode == 200 || responseCode == 201 || responseCode == 409 ) {return static_cast<int>(CurlReturnCode::SUCCESS);}// 其他错误return static_cast<int>(CurlReturnCode::ERROR_INVALID_RESPONSE);}catch (const std::exception& ex) {//logger.logMessage(std::string("createFolderInBucket: EXCEPTION: ") + ex.what());return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}static int progressCallback(void* clientp,curl_off_t dltotal, curl_off_t dlnow,curl_off_t ultotal, curl_off_t ulnow)
{static auto lastPrintTime = std::chrono::steady_clock::now();auto now = std::chrono::steady_clock::now();auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastPrintTime).count();if (elapsed >= 500) { // 500毫秒刷一次CURL* curl = static_cast<CURL*>(clientp);double uploadSpeedBytes = 0.0;if (curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &uploadSpeedBytes) == CURLE_OK) {double uploadSpeedMbit = (uploadSpeedBytes  ) / 1000000.0;double percent = (ultotal > 0) ? (static_cast<double>(ulnow) / ultotal) * 100.0 : 0.0;printf("\r上传进度:%.2f%% (%.2f Mb / %.2f Mb)  速度:%.2f Mb/s",percent,(ulnow  ) / 1000000.0,(ultotal  ) / 1000000.0,uploadSpeedMbit);fflush(stdout);}lastPrintTime = now;}return 0;
}int CurlMinIOApi::uploadFileToFolderInSite(const std::string& bucketName,const std::string& folderName, const std::string& localFilePath, const std::string& fileName) {if (bucketName.empty() || folderName.empty() || localFilePath.empty() ||fileName.empty()) {//logger.logMessage("uploadFileToFolderInBucket: ERROR_INVALID_INPUT: Empty input parameters");return static_cast<int>(CurlReturnCode::ERROR_INVALID_INPUT);}CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {//logger.logMessage("uploadFileToFolderInBucket: ERROR_CURL_INIT: curl_global_init failed");return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;// 确保文件路径以"/"结尾表示文件夹std::string objectKey = folderName;if (objectKey.back() != '/') {objectKey += '/';}objectKey += fileName; // 文件的完整路径std::string url = baseUrl + bucketName + "/" + objectKey;// 读取文件FILE* file = fopen(localFilePath.c_str(), "rb");if (!file) {//logger.logMessage("uploadFileToFolderInBucket: ERROR_FILE_OPEN: Unable to open file");return static_cast<int>(CurlReturnCode::ERROR_FILE_OPEN);}  #if defined(_WIN32) || defined(_WIN64)_fseeki64(file, 0, SEEK_END);curl_off_t fileSize = _ftelli64(file);_fseeki64(file, 0, SEEK_SET);#elsefseeko(file, 0, SEEK_END);curl_off_t fileSize = ftello(file);fseeko(file, 0, SEEK_SET);#endifstd::string canonicalRequest ="PUT\n""/" + bucketName + "/" + objectKey + "\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(region, service,canonicalRequestHash, utcTime, utcDate);// 设置请求头headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, "PUT");curl_easy_setopt(curl.get(), CURLOPT_UPLOAD, 1L);curl_easy_setopt(curl.get(), CURLOPT_READDATA, file);curl_easy_setopt(curl.get(), CURLOPT_INFILESIZE_LARGE, fileSize);curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);// 设置传输进度回调函数   if (0) {curl_easy_setopt(curl.get(), CURLOPT_NOPROGRESS, 0L); // 必须启用进度回调curl_easy_setopt(curl.get(), CURLOPT_XFERINFOFUNCTION, progressCallback); // 设置回调curl_easy_setopt(curl.get(), CURLOPT_XFERINFODATA, curl.get()); // 把curl指针传给回调}// 执行上传CURLcode res = curl_easy_perform(curl.get());fclose(file);if (res != CURLE_OK) {//logger.logMessage("uploadFileToFolderInBucket: ERROR_CURL_PERFORM: " + std::string(curl_easy_strerror(res)));return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}long responseCode;curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &responseCode);// 成功上传if (responseCode == 200 || responseCode == 201) {return static_cast<int>(CurlReturnCode::SUCCESS);}// 409 Conflict:文件已存在if (responseCode == 409) {return static_cast<int>(CurlReturnCode::ERROR_CONFLICT);}// 其他错误return static_cast<int>(CurlReturnCode::ERROR_INVALID_RESPONSE);}catch (const std::exception& ex) {//logger.logMessage(std::string("uploadFileToFolderInBucket: EXCEPTION: ") + ex.what());return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}int CurlMinIOApi::downloadFileFromFolderInSite(const std::string& bucketName,const std::string& folderName, const std::string& remoteFileName, const std::string& localSavePath) {if (bucketName.empty() || folderName.empty() || remoteFileName.empty() || localSavePath.empty()) {return static_cast<int>(CurlReturnCode::ERROR_INVALID_INPUT);}CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;std::string objectKey = folderName;if (objectKey.back() != '/') {objectKey += '/';}objectKey += remoteFileName;std::string url = baseUrl + bucketName + "/" + objectKey;// 构建签名std::string canonicalRequest ="GET\n""/" + bucketName + "/" + objectKey + "\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(/*accessKey, secretKey,*/ region, service,canonicalRequestHash, utcTime, utcDate);// 设置请求头headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());// 打开本地文件,用来保存下载内容FILE* file = fopen(localSavePath.c_str(), "wb");if (!file) {return static_cast<int>(CurlReturnCode::ERROR_FILE_OPEN);}curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L); // GET方法curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, nullptr); // 默认写入回调curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, file); // 写到文件curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);// 执行下载CURLcode res = curl_easy_perform(curl.get());fclose(file);if (res != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}long responseCode;curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &responseCode);if (responseCode == 200) {return static_cast<int>(CurlReturnCode::SUCCESS);}// 404找不到文件if (responseCode == 404) {return static_cast<int>(CurlReturnCode::ERROR_CURL_FILE_NOT_FOUND);}return static_cast<int>(CurlReturnCode::ERROR_INVALID_RESPONSE);}catch (const std::exception& ex) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}int CurlMinIOApi::checkBucketExists(const std::string& bucketName) {if (bucketName.empty()) {return static_cast<int>(CurlReturnCode::ERROR_INVALID_INPUT);}CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;std::string url = baseUrl + bucketName;std::string canonicalRequest ="HEAD\n""/" + bucketName + "\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(region, service, canonicalRequestHash, utcTime, utcDate);headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_NOBODY, 1L); // HEAD 请求curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);CURLcode res = curl_easy_perform(curl.get());if (res != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}long responseCode;curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &responseCode);if (responseCode == 200) {return static_cast<int>(CurlReturnCode::SUCCESS);}if (responseCode == 404) {return static_cast<int>(CurlReturnCode::ERROR_BUCKET_NOT_FOUND);}return static_cast<int>(CurlReturnCode::ERROR_INVALID_RESPONSE);}catch (const std::exception& ex) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}int CurlMinIOApi::checkFolderExists(const std::string& bucketName, const std::string& folderName) {if (bucketName.empty() || folderName.empty()) {return static_cast<int>(CurlReturnCode::ERROR_INVALID_INPUT);}CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;std::string objectKey = folderName;if (objectKey.back() != '/') {objectKey += '/';}std::string url = baseUrl + bucketName + "/" + objectKey;std::string canonicalRequest ="HEAD\n""/" + bucketName + "/" + objectKey + "\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(region, service, canonicalRequestHash, utcTime, utcDate);headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_NOBODY, 1L); // HEAD请求curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);CURLcode res = curl_easy_perform(curl.get());if (res != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}long responseCode;curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &responseCode);if (responseCode == 200) {return static_cast<int>(CurlReturnCode::SUCCESS);}if (responseCode == 404) {return static_cast<int>(CurlReturnCode::ERROR_FOLDER_NOT_FOUND);}return static_cast<int>(CurlReturnCode::ERROR_INVALID_RESPONSE);}catch (const std::exception& ex) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}int CurlMinIOApi::checkFileExists(const std::string& bucketName, const std::string& folderName, const std::string& fileName) {if (bucketName.empty() || folderName.empty() || fileName.empty()) {return static_cast<int>(CurlReturnCode::ERROR_INVALID_INPUT);}CURLcode globalInit = curl_global_init(CURL_GLOBAL_ALL);if (globalInit != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}try {CurlHandle curl;CurlSlist headers;std::string region = "us-east-1";std::string service = "s3";std::string utcTime = get_utc_time();std::string utcDate = get_utc_date();std::string host = sysHost;// 构造对象Keystd::string objectKey = folderName;if (objectKey.back() != '/') {objectKey += '/';}objectKey += fileName;// URL拼接std::string url = baseUrl + bucketName + "/" + objectKey;// 构建CanonicalRequeststd::string canonicalRequest ="HEAD\n""/" + bucketName + "/" + objectKey + "\n""\n""host:" + host + "\n""x-amz-content-sha256:UNSIGNED-PAYLOAD\n""x-amz-date:" + utcTime + "\n""\n""host;x-amz-content-sha256;x-amz-date\n""UNSIGNED-PAYLOAD";std::string canonicalRequestHash = sha256_hex(canonicalRequest);std::string authorization = buildAuthorization(region, service, canonicalRequestHash, utcTime, utcDate);// 设置请求头headers.get() = curl_slist_append(headers.get(), ("Host: " + host).c_str());headers.get() = curl_slist_append(headers.get(), "x-amz-content-sha256: UNSIGNED-PAYLOAD");headers.get() = curl_slist_append(headers.get(), ("x-amz-date: " + utcTime).c_str());headers.get() = curl_slist_append(headers.get(), ("Authorization: " + authorization).c_str());// 设置CURLcurl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());curl_easy_setopt(curl.get(), CURLOPT_NOBODY, 1L); // 关键:HEAD请求,不下载内容curl_easy_setopt(curl.get(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, 0L);curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, 0L);curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0L);// 执行请求CURLcode res = curl_easy_perform(curl.get());if (res != CURLE_OK) {return static_cast<int>(CurlReturnCode::ERROR_CURL_PERFORM);}long responseCode;curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &responseCode);// 判断返回if (responseCode == 200) {return static_cast<int>(CurlReturnCode::SUCCESS); // 文件存在}if (responseCode == 404) {return static_cast<int>(CurlReturnCode::ERROR_CURL_FILE_NOT_FOUND); // 文件不存在}return static_cast<int>(CurlReturnCode::ERROR_INVALID_RESPONSE); // 其他错误}catch (const std::exception& ex) {return static_cast<int>(CurlReturnCode::ERROR_CURL_INIT);}
}
CurlMinIOApi.h的内容
#pragma once
#include <curl/curl.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iostream>
#include <sys/stat.h> 
#include <filesystem>
#include <windows.h>
#include "Base64Utils.h"
#include "serverHelp.h"typedef void (*ProgressCallback)(int);
class CurlMinIOApi {
public:CurlMinIOApi(const std::string& m_publicKey);~CurlMinIOApi();int checkMinIOServerHealth();int createSite(const std::string& bucketName);int createFolderInSite(const std::string& bucketName,const std::string& folderName);int uploadFileToFolderInSite(const std::string& bucketName,const std::string& folderName, const std::string& localFilePath, const std::string& fileName);int downloadFileFromFolderInSite(const std::string& bucketName,const std::string& folderName, const std::string& remoteFileName, const std::string& localSavePath);int checkBucketExists(const std::string& bucketName);int checkFolderExists(const std::string& bucketName, const std::string& folderName);int checkFileExists(const std::string& bucketName, const std::string& folderName, const std::string& fileName);bool isValid;private:std::string buildAuthorization(const std::string& region, const std::string& service,const std::string& canonicalRequestHash,const std::string& utcTime, const std::string& utcDate);std::string hmac_sha256(const std::string&, const std::string&);std::string sha256_hex(const std::string&);std::string to_hex(const std::string&);   std::string get_utc_time();std::string get_utc_date();std::string sysHost;std::string baseUrl;std::string accessKey;std::string secretKey;
};
  1. 辅助的类和函数
1.serverHelp.h内容
#pragma once
#include <fstream> 
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>#include <iostream> 
#include <vector> 
#include <string>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream> 
#include <curl/curl.h>
// ----------------- RAII 封装 ----------------
class CurlHandle {
public:CurlHandle() : curl_(curl_easy_init()) {if (!curl_) {throw std::runtime_error("curl_easy_init failed");}}~CurlHandle() {if (curl_) {curl_easy_cleanup(curl_);}}CURL* get() { return curl_; }
private:CURL* curl_;
};class CurlSlist {
public:CurlSlist() : slist_(nullptr) {}~CurlSlist() {if (slist_) {curl_slist_free_all(slist_);}}struct curl_slist*& get() { return slist_; }
private:struct curl_slist* slist_;
};// ----------------- 返回码定义 ----------------
enum class CurlReturnCode {SUCCESS = 0,ERROR_CURL_INIT = -410,ERROR_CURL_PERFORM = -510,ERROR_INVALID_RESPONSE = -520,ERROR_FORBIDDEN = -521,ERROR_INVALID_INPUT = -530,ERROR_UNAUTHORIZED = -531,ERROR_REQUEST_NOT_FOUND = -532,ERROR_METHOD_NOT_ALLOWED = -533,ERROR_CONFLICT = -534,ERROR_EMPTY_DOWNLOAD = -535,ERROR_CURL_SETOPT = -536,ERROR_FILE_OPEN = -537,ERROR_FILE_READ = -538,ERROR_INSUFFICIENT_STORAGE = -670,ERROR_CONVERT_QBYTEARRY_TO_FILE = -671,ERROR_CURL_FILE_NOT_FOUND = -672,ERROR_BUCKET_NOT_FOUND = -673,ERROR_FOLDER_NOT_FOUND = -674,ERROR_UPLOAD_INIT = -675, ERROR_PART_UPLOAD = -676, ERROR_UPLOAD_COMPLETE = -677,ERROR_SERVER_UNAVAILABLE = -710
};
2.Base64Utils.h的内容
#pragma once#include <string>
#include <stdexcept>  // ← 加这个
#include <utility>    // ← 如果用 std::pair
#include <cstddef>    // ← 如果用 size_tclass Base64Utils {
public:// 编码static std::string encode(const std::string& input);// 解码static std::string decode(const std::string& input);// 新增:解码并拆分 accessKey 和 secretKey//static std::pair<std::string, std::string> decodeAndSplit(const std::string& encodedInput);static std::pair<bool, std::string> decodeAndSplit(const std::string& encodedInput,std::string& outAccessKey,std::string& outSecretKey);private:static const std::string base64_chars;static inline bool isBase64(unsigned char c);
};

3.Base64Utils.cpp的内容

#include "Base64Utils.h"const std::string Base64Utils::base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";inline bool Base64Utils::isBase64(unsigned char c) {return (isalnum(c) || (c == '+') || (c == '/'));
}std::string Base64Utils::encode(const std::string& input) {unsigned char const* bytes_to_encode = reinterpret_cast<const unsigned char*>(input.c_str());unsigned int in_len = input.length();std::string ret;int i = 0;int j = 0;unsigned char char_array_3[3];unsigned char char_array_4[4];while (in_len--) {char_array_3[i++] = *(bytes_to_encode++);if (i == 3) {char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);char_array_4[3] = char_array_3[2] & 0x3f;for (i = 0; i < 4; i++)ret += base64_chars[char_array_4[i]];i = 0;}}if (i) {for (j = i; j < 3; j++)char_array_3[j] = '\0';char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);char_array_4[3] = char_array_3[2] & 0x3f;for (j = 0; (j < i + 1); j++)ret += base64_chars[char_array_4[j]];while ((i++ < 3))ret += '=';}return ret;
}std::string Base64Utils::decode(const std::string& encoded_string) {int in_len = encoded_string.size();int i = 0;int j = 0;int in_ = 0;unsigned char char_array_4[4], char_array_3[3];std::string ret;while (in_len-- && (encoded_string[in_] != '=') && isBase64(encoded_string[in_])) {char_array_4[i++] = encoded_string[in_];in_++;if (i == 4) {for (i = 0; i < 4; i++)char_array_4[i] = static_cast<unsigned char>(base64_chars.find(char_array_4[i]));char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];for (i = 0; (i < 3); i++)ret += char_array_3[i];i = 0;}}if (i) {for (j = i; j < 4; j++)char_array_4[j] = 0;for (j = 0; j < 4; j++)char_array_4[j] = static_cast<unsigned char>(base64_chars.find(char_array_4[j]));char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];for (j = 0; (j < i - 1); j++)ret += char_array_3[j];}return ret;
}// 返回:pair { 是否成功, 错误信息/成功时返回"" }
std::pair<bool, std::string> Base64Utils::decodeAndSplit(const std::string& encodedInput,std::string& outAccessKey,std::string& outSecretKey
) {outAccessKey.clear();outSecretKey.clear();if (encodedInput.empty()) {return { false, "decodeAndSplit: input is empty" };}std::string decoded = decode(encodedInput);if (decoded.empty()) {return { false, "decodeAndSplit: base64 decode failed" };}size_t pos = decoded.find(':');if (pos == std::string::npos) {return { false, "decodeAndSplit: decoded string missing ':' separator" };}outAccessKey = decoded.substr(0, pos);outSecretKey = decoded.substr(pos + 1);if (outAccessKey.empty() || outSecretKey.empty()) {return { false, "decodeAndSplit: accessKey or secretKey is empty" };}return { true, "" };
}
main函数
std::string siteName = "eala";
std::string folderName = "myfolde1rala";std::string objectName = "12.txt";//MINIO云
void main() {std::string accessKey = "qVQ";std::string secretKey = "SOkr4dZ3EIO";std::string base64Auth = Base64Utils::encode(accessKey + ":" + secretKey);CurlMinIOApi* minioApi = new CurlMinIOApi(base64Auth);if (!minioApi->isValid) return;int result = -1;result = minioApi->checkMinIOServerHealth();if (0 == result)   std::cout << "checkMinIOServerHealth成功!" << std::endl;result = minioApi->createSite(siteName);if (0 == result) {std::cout << "Site创建成功或已经存在!" << std::endl;result = minioApi->createFolderInSite(siteName, folderName);if (result == 0) {std::cout << "文件夹创建成功或已经存在!" << std::endl;result = minioApi->uploadFileToFolderInSite(siteName, folderName, filePath, objectName);//result = minioApi->uploadLargeFileToFolderInSite(siteName, folderName, filePath, objectName);if (result == 0) {std::cout << "文件上传成功!" << std::endl;result = minioApi->checkBucketExists(siteName);if (result == 0) std::cout << "checkBucketExists!" << std::endl;result = minioApi->checkFolderExists(siteName,folderName);if (result == 0) std::cout << "checkFolderExists!" << std::endl; result = minioApi->checkFileExists(siteName,folderName, objectName);if (result == 0) std::cout << "checkFileExists!" << std::endl;result = minioApi->downloadFileFromFolderInSite(siteName, folderName,  objectName,"abc.txt");if (result == 0) std::cout << "文件下载成功!" << std::endl;}}}return;
}

一些点

1.

我为了和自己的其他项目做兼容性,再main函数中,对key和Secret做了基于base64的加密,在使用中,反解析出来,其中两个内容使用冒号进行拼接的。你如果想方便,直接传进去也可以。

2.

我的Minio服务器的endpoint实在默认初始化时写死的,最好当然是使用配置文件,这样就比较方便的在不修改代码的情况下配置不同的服务器。
在这里插入图片描述

写在最后

主要是为了给自己做记录。

相关文章:

  • 30天通过软考高项-第六天
  • MTKAndroid12-13-开机应用自启功能实现
  • Vue 对话框出现时,为什么滚动鼠标还是能滚动底层元素
  • Spring系列四:AOP切面编程第三部分
  • 软件工程(一):黑盒测试与白盒测试
  • 如何在WordPress网站中设置双重验证,提升安全性
  • 打火机检测数据集VOC+YOLO格式925张1类别
  • 案例篇:如何用tcpdump和Wireshark识别潜在威胁
  • Finish技术生态计划: FinishRpc
  • 无线采发仪多通道 在结构健康与地质灾害监测中的应用 VS-Box振弦、温度及多信号采发仪
  • 【Vue.js】Vue3父子组件数据通信案例(基础案例)
  • Vue组件开发进阶:从通信原理到DOM异步更新实战
  • 【北京】昌平区某附小v3700存储双控故障维修案例
  • leetcode刷题日记——两数相加
  • C++20 小语法
  • Dockerfile 编写根据需求阶段而出现
  • 【Redis】基础4:作为分布式锁
  • 克隆/备份后的虚拟机无法获取IP地址(FQA)
  • 微软编程一小时:探索 AI 世界
  • Prometheus 实战教程-搭建 Prometheus 环境
  • 三位成功女性,如何应对失败
  • 物业也能成为居家养老“服务员”,上海多区将开展“物业+养老”试点
  • 人民时评:投资于人,促高质量充分就业
  • 加总理:目前没有针对加拿大人的“活跃威胁”
  • 上海市委常委会传达学习总书记重要讲话精神,研究张江科学城建设等事项
  • 靳燕出任中央戏剧学院党委副书记,原任中戏院长助理