使用Curl进行本地MinIO的操作
前言
最近在做相关的项目中关于本地服务搭建和访问的技术验证,打进来最基本的数据访问,使用了C++。可以进行:服务器的可用性检查、Bucket的创建、文件夹的创建、文件的上传、文件的下载、文件夹和Bucket的存在性检查等基本接口,对自己做记录,同时希望能帮到有需要的朋友。法布施得智慧嘛!
环境
1.开发环境
vs2019
2.第三方库
第三方库链接:
https://download.csdn.net/download/weixin_40523119/90713265
核心代码
- 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.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实在默认初始化时写死的,最好当然是使用配置文件,这样就比较方便的在不修改代码的情况下配置不同的服务器。
写在最后
主要是为了给自己做记录。
相关文章:
使用Curl进行本地MinIO的操作
前言 最近在做相关的项目中关于本地服务搭建和访问的技术验证,打进来最基本的数据访问,使用了C。可以进行:服务器的可用性检查、Bucket的创建、文件夹的创建、文件的上传、文件的下载、文件夹和Bucket的存在性检查等基本接口,对自…...
uniswap getTickAtSqrtPrice 方法解析
先上代码: function getTickAtSqrtPrice(uint160 sqrtPriceX96) internal pure returns (int24 tick) {unchecked {// Equivalent: if (sqrtPriceX96 < MIN_SQRT_PRICE || sqrtPriceX96 > MAX_SQRT_PRICE) revert InvalidSqrtPrice();// second inequality mu…...
qemu(3) -- qemu-user使用
1. 前言 qemu中有很多的特技,此处记录下qemu-arm的使用方式,简单来说qemu-system-xx用于虚拟整个设备,包括操作系统的运行环境,而qemu-xx仅虚拟Linux应用程序的环境,不涉及操作系统,应用程序的系统调用有宿…...
CMCC RAX3000M使用Tftpd刷写OpenWrt固件的救砖方法
有时候,我们在玩运行 OpenWrt 的 CMCC RAX3000M ,因为一些操作不当,导致无法进入路由器系统,无法正常刷机。此时,如果我们已经刷写了uboot,则可以在uboot模式下通过Tftpd刷写新的OpenWrt固件,实…...
Vue基础(7)_计算属性
计算属性(computed) 一、使用方式: 1.定义计算属性: 在Vue组件中,通过在 computed 对象中定义计算属性名称及对应的计算函数来创建计算属性。计算函数会返回计算属性的值。 2.在模板中使用计算属性: 在Vue的模板中,您…...
1.9多元函数积分学
引言 多元函数积分学是考研数学一的核心内容,涵盖三重积分、曲线积分、曲面积分及空间曲线积分。本文系统梳理4大考点,结合公式速查与典型示例,助你高效攻克积分难题! 考点一:三重积分计算与应用 1️⃣ 对称性 (1) …...
【LINUX操作系统】线程操作
了解了线程的基本原理之后,我们来学习线程在C语言官方库中的写法与用法。 1. 常见pthread接口及其背后逻辑 1.1 pthread_create 与线程有关的函数构成了⼀个完整的系列,绝⼤多数函数的名字都是以“pthread_”打头的 • 要使⽤这些函数库,…...
web技术与nginx网站环境部署
一:web基础 1.域名和DNS 1.1域名的概念 网络是基于TCP/IP协议进行通信和连接的,每一台主机都有一个唯一的标识(固定的IP地址),用以区别在网络上成千上万个用户和计算机。网络在区分所有与之相连的网络和主机时,均采用一种唯一、通用的地址…...
多元复合函数求导的三种情况
1. 一元函数与多元函数复合 1.1 变量关系 1.2 求导公式 因为根据链式法则,先对z求导,z是二元函数,所以说是偏导;再对里面求导,u、v是一元函数,所以说是全导。 2. 多元函数与多元函数复合 2.1 变量关系…...
全面解析DeepSeek算法细节(2) —— 多令牌预测(Multi Token Prediction)
概述 多令牌预测(MTP)技术使DeepSeek-R1能够并行预测多个令牌,显著提升推理速度。 关键特性 并行多令牌预测:DeepSeek-R1通过同时预测多个令牌而非按顺序预测,提升了推理速度。这减少了解码延迟,在不影响…...
如何从大规模点集中筛选出距离不小于指定值的点
一、背景:当点集处理遇见效率挑战 在数字化浪潮席卷各行各业的今天,点集数据处理已成为地理信息系统(GIS)、计算机视觉、粒子物理仿真等领域的核心需求。以自动驾驶场景为例,激光雷达每秒可产生超过10万个点云数据&am…...
单片机-89C51部分:6、数码管
飞书文档https://x509p6c8to.feishu.cn/wiki/WRNLwDd0iiG8OWkyatOcom6knHf 一、数码管简介 通俗解释: 一个数码管等于八个LED组合在一起,想要显示什么形状,就点亮对应LED即可。 一般数码管分为共阴极数码管和共阳极数码管。 共阳极接法&…...
可解释人工智能(XAI):让机器决策透明化
在人工智能(AI)技术飞速发展的今天,AI 系统已经广泛应用于金融、医疗、交通等多个关键领域。然而,随着 AI 系统的复杂性不断增加,尤其是深度学习模型的广泛应用,AI 的“黑箱”问题逐渐凸显。AI 系统的决策过…...
深入理解网络原理:TCP协议详解
在现代计算机网络中,传输控制协议(TCP,Transmission Control Protocol)是最常用的传输层协议之一。TCP被广泛应用于互联网中的许多关键应用,如网页浏览、电子邮件和文件传输等。作为一种面向连接的协议,TCP…...
二极管钳位电路——Multisim电路仿真
目录 二极管钳位电路 2.1 二极管正向钳位电路 二极管压降测试 2.1.1 二极管正向钳位电路图 2.1.2 二极管正向钳位工作原理 2.2 二极管负向钳位电路 2.2.1 二极管负向钳位电路图 2.2.2 二极管负向钳位工作原理 二极管正向反向钳位仿真电路实验结果 2.3 二极管顶部钳位…...
【更新】LLM Interview (2)
字数溢出,不解释 前文:llm interview (1) 文章目录 强化学习专题1 什么是RL?2 RL和监督、非监督、深度学习的区别3 RL中所谓的损失函数与深度学习中的损失函数有何区别?4 RL历史5 RL分类5.1 分类图示5.2 根据智能体动作选取方式分…...
第二节:文件系统
理论知识 文件系统的基本概念:文件系统是操作系统中负责管理持久数据的子系统,它将数据组织成文件和目录的形式,方便用户存储和访问数据。Linux文件系统的类型:常见的 Linux 文件系统类型有 Ext2、Ext3、Ext4、XFS、Btrfs 等。Ex…...
astrbot_plugin_composting_bucket开源程序是一个用于降低AstrBot的deepseek api调用费用的插件
一、软件介绍 文末提供程序和源码下载 astrbot_plugin_composting_bucket开源程序是一个用于降低AstrBot的deepseek api调用费用的插件,让deepseek api调用费用更低! 本插件功能已集成到 AstrBot ,您可以移除此插件,在 AstrBot…...
8.Three.js中的 StereoCamera 立体相机详解+示例代码
✨ 运行效果 👀 左边一幅图、右边一幅图,略微偏移,形成立体感~ (戴上VR眼镜或红蓝3D眼镜体验更明显哦~) 🔥 小球或方块旋转中,左右略微不同步,立体感更强&am…...
MYSQL——时间字段映射Java类型
在 Java 中查询数据库中的【时间字段】时,可以使用以下几种类型来处理: 1. java.sql.Date 适用场景:当数据库中的时间字段是 date 类型时,使用 java.sql.Date 是最合适的选择。示例代码:ResultSet rs statement.exe…...
搭建speak yarn集群:从零开始的详细指南
在大数据处理领域,Apache Spark 是一个高性能的分布式计算框架,而 YARN(Yet Another Resource Negotiator)是 Hadoop 的资源管理器。将 Spark 集成到 YARN 中,不仅可以充分利用 Hadoop 的资源管理能力,还能…...
第十三章-PHP MySQL扩展
第十三章-PHP与MySQL 一,连接数据库 1. 使用 MySQLi(面向对象方式) <?php // 数据库参数 $host localhost; $username root; $password ; $database test_db;// 创建连接 $conn new mysqli($host, $username, $password, $databa…...
在服务器中,搭建FusionCompute,实现集群管理
序:需要自备一台服务器,并安装部署好KVM,自行下载镜像,将所需的CNA和VRM镜像放到服务器中,小编所用的进项版本如下,读者可自行根据需求下载其它版本的镜像。 CNA镜像:FusionCompute_CNA-8.3.0-…...
嵌入式开发学习日志Day11
一、函数的递归调用 在调用一个函数的过程中,又出现直接或者间接的调用函数本身,称之为函数的递归调用; 函数的递归调用是使用大量的内存空间完成程序进行的; 1.间接调用 2.直接调用 注意: 上图仅为示意,…...
【线性规划】对偶问题的实际意义与重要性质 学习笔记
【线性规划】对偶问题的实际意义与重要性质_哔哩哔哩_bilibili...
代码随想录第30天:动态规划3
一、01背包理论基础(Kama coder 46) “01背包”:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 1. 确…...
DSP48E2 的 MAC模式功能仿真
DSP48E2 仿真代码: 测试的功能为 P i ( A D ) ∗ B P i − 1 P_{i} (AD) * B P_{i-1} Pi(AD)∗BPi−1 timescale 1ns / 1nsmodule dsp_tb;// 输入reg CLK;reg CE;reg SCLR;reg signed [26:0] A, D;reg signed [17:0] B;// 输出wire signed [47:0] P;par…...
【环境配置】Mac电脑安装运行R语言教程 2025年
一、安装 Xcode Command Line Tools 打开终端,输入如下命令: xcode-select --install安装完成后,输入如下命令,能看见版本号说明安装成功 gcc --version二、下载安装R语言 https://mirrors.tuna.tsinghua.edu.cn/CRAN/ 点开后…...
常见算法的总结与实现思路
前言 hello,我是Maybe。昨天和今天花了两天左右的时间。把常见的排序算法都学完了,自己也实现了一遍。感觉收获满满,但是过程是艰辛的。下面我将分享代码和思维导图,希望可以帮助到大家。 思维导图(含注意事项,实现思…...
Ethan独立开发产品日报 | 2025-04-27
1. CreateWise AI 旨在提升你工作效率的AI播客编辑器 人工智能播客编辑器,让你的播客制作速度提升10倍!它可以自动去除口头语和沉默,生成节目笔记和精彩片段,还能一键制作适合社交媒体分享的短视频——所有这些功能都只需一次点…...
5G与边缘计算:协同发展,开启智慧世界新篇章
**5G与边缘计算:协同发展,开启智慧世界新篇章 ** 大家好,我是Echo_Wish。今天我们来探讨一个备受关注的技术话题——5G与边缘计算的协同发展。随着5G网络的逐步普及以及边缘计算技术的快速发展,二者的结合为我们带来了前所未有的创…...
AcWing 885:求组合数 I ← 杨辉三角
【题目来源】 https://www.acwing.com/problem/content/887/ 【题目描述】 给定 n 组询问,每组询问给定两个整数 a,b,请你输出 C(a,b) mod (10^97) 的值。 【输入格式】 第一行包含整数 n。 接下来 n 行,每行包含一组 a 和 b。 …...
Python3:Jupyterlab 安装和配置
Python3:Jupyterlab 安装和配置 Jupyter源于Ipython Notebook项目,是使用Python(也有R、Julia、Node等其他语言的内核)进行代码演示、数据分析、机器学习、可视化、教学的非常好的工具。 最新的基于web的交互式开发环境,适用于n…...
如何搭建spark yarn模式的集合集群
一、环境准备 在搭建 Spark on YARN 集群之前,需要确保以下环境已经准备就绪: 操作系统:推荐使用 CentOS、Ubuntu 等 Linux 发行版。 Java 环境:确保安装了 JDK 1.8 或更高版本。 Hadoop 集群:已经搭建并运行的 Had…...
智能座舱架构中芯片算力评估
在智能座舱(Intelligent Cockpit)领域,芯片的算力是决定系统性能、响应速度以及用户体验的关键因素之一。 随着汽车智能化程度的不断提高,智能座舱对芯片的算力、功耗、集成度以及安全性提出了更高的要求。 智能座舱架构中芯片算…...
STM32完整内存地址空间分配详解
在STM32这类基于ARM Cortex-M的32位微控制器中,整个4GB的地址空间(从0x00000000到0xFFFFFFFF)有着非常系统化的分配方案,每个区域都有其特定的用途。下面我将详细介绍这些地址区域的分配及其功能: STM32完整内存地址空间分配详解(0x00000000…...
叉车司机N1考试的实操部分有哪些注意事项?
叉车司机 N1 考试实操部分分为场地考试和场内道路考试,以下是一些注意事项: 场地考试 起步:检查车辆仪表和个人仪容,穿好工作服、戴安全帽,不穿拖鞋等不符规定的鞋。同时检查换挡和换向操纵杆在空档位置,…...
【行业特化篇2】金融行业简历特化指南:合规性要求与风险控制能力的艺术化呈现
写在最前 作为一个中古程序猿,我有很多自己想做的事情,比如埋头苦干手搓一个低代码数据库设计平台(目前只针对写java的朋友),比如很喜欢帮身边的朋友看看简历,讲讲面试技巧,毕竟工作这么多年,也做到过高管,有很多面人经历,意见还算有用,大家基本都能拿到想要的offe…...
Linux 定时备份到windows 方案比较
1 传输协议比较 特性SCPRSYNCSFTP基本功能文件传输(本地与远程)文件和目录的同步与传输文件管理(上传、下载、删除等)增量传输不支持增量传输支持增量传输不支持增量传输性能传输速度较慢,效率低高效,适合…...
【网络编程】TCP/IP四层模型、MAC和IP
1. TCP/IP的四层模型 网络模型的目的:规范通信标准,确保不同设备和系统之间能够有效通信 对比OSI模型与TCP/IP模型: OSI模型的七层架构(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层)TCP/IP模型的四…...
Java学习手册: IoC 容器与依赖注入
一、IoC 容器概述 IoC(Inversion of Control,控制反转)容器是 Spring 框架的核心组件之一。它负责创建对象、管理对象的生命周期以及对象之间的依赖关系。通过将对象的创建和管理交给 IoC 容器,开发者可以实现代码的松耦合&#…...
Web 基础与Nginx访问统计
目录 Web基础 域名与DNS 域名的结构 网页与HTML 网页概述 HTML 概述 HTML基本标签 1、HTML 语法规则 2、HTML 文件结构 静态网页和动态网页 HTTP协议概述 HTTP方法 HTTP状态码 Nginx访问状态统计 Web基础 域名与DNS 网络是基于 TCP/IP 协议进行通信和连接的,每一台主机都有一…...
了解Android studio 初学者零基础推荐(1)
线上学习课程链接 开发Andorid App 使用的语言有很多,包括java, kotlin,C,等,首先让我们了解kotlin这个热门语言。 kotlin 程序 fun main() {println("hello,xu") } kotlin中的函数定义语法:函数名称在fun关键字后面࿰…...
Android Studio 2024版,前进返回按钮丢失解决
最近升级完AS最新系统后,顶部的前进和返回按钮默认隐藏了 解决方案: 1. 打开settings 2. 找到左侧 Appearance & Behavior 下面点击 Menus and Toolbars 3. 点击 Main Toolar 4. 点击Left,右键选择 Add Actions 5. 弹框中选择 Main Me…...
详解UnityWebRequest类
什么是UnityWebRequest类 UnityWebRequest 是 Unity 引擎中用于处理网络请求的一个强大类,它可以让你在 Unity 项目里方便地与网络资源进行交互,像发送 HTTP 请求、下载文件等操作都能实现。下面会详细介绍 UnityWebRequest 的相关内容。 UnityWebRequ…...
安装qt4.8.7
QT4.8.7安装详细教程(MinGW 4.8.2和QTCreator4.2.0)_qtcreater482-CSDN博客 QT4.8.7安装详细教程(MinGW 4.8.2和QTCreator4.2.0) 1、下载 1)下载QT4.8.7 http://download.qt.io/archive/ 名称:qt-opensource-windows-x86-mingw482…...
2025系统架构师---管道/过滤器架构风格
引言 在分布式系统与数据密集型应用主导技术演进的今天,管道/过滤器架构风格(Pipes and Filters Architecture Style)凭借其数据流驱动、组件解耦与并行处理能力,成为处理复杂数据转换任务的核心范式。从Unix命令…...
仙宫云ComfyUI —【Wan2.1】AI视频生成部署
【Wan2.1】AI视频生成本地部署与使用技巧全面详解_哔哩哔哩_bilibili 所有模型下载:https://pan.quark.cn/s/9d793aa1b258 Runninghub本期课程工作流下载(可获得1000RH币):https://www.runninghub.cn/?utm_sourcekol01-RH145 仙…...
学成在线。。。
一:讲师管理 介绍:可以实现对讲师的分页展示,多条件组合分页查询,对讲师的添加,修改,删除操作。 针对于添加来说,使用requestBody注解,搭配postmapping接收数据,使用service层的对象,调用mapper方法,向数据库中保存数据。 修改: 先根据讲师id,查询出讲师,再去…...
Python爬虫实战:获取猫yan电影网最新热门电影数据并做分析,为51观影做参考
一、引言 随着互联网的迅速发展,电影信息获取更加便捷。猫yan电影作为国内知名电影信息平台,提供了丰富电影数据。对于我们而言,获取并分析这些数据,能为用户提供更有价值的观影建议。本文详细介绍使用 Python 的 Scrapy 框架实现猫yan电影数据爬取与分析,为 “五一” 观…...