最近更新时间:2025-04-25 15:42:55
using namespace ks3::sdk;
void putObjectByUrlDemo() {
std::string filename = "<your file path>";
std::string ak = "<your access key>";
std::string sk = "<your secret key>";
std::string host = "<your endpoint>"; // 填写桶所在region对应的Endpoint
// 初始化client
KS3Client client(host, ak, sk);
std::string bucketName = "<your bucket name>";
std::string keyName = "<your key name>";
auto req = GeneratePresignedUrlRequest(bucketName, keyName, MethodType::PUT_METHOD);
// 设置过期时间为1小时
req.SetExpires(std::time(nullptr) + 3600);
std::string uploadUrl = client.GeneratePresignedUrl(req);
long fileSize = getFileSize(filename);
std::shared_ptr<std::iostream> content = std::make_shared<std::fstream>(filename, std::ios::in|std::ios::binary);
Response resp = doRequest({0, fileSize, content, {}, "PUT", uploadUrl});
std::cout << "status code: " << resp.status_code << std::endl;
}
using namespace ks3::sdk;
std::string generateInitMultipartUploadPreSignedUrl(const KS3Client& client, const std::string& bucketName, const std::string& keyName, const std::map<std::string, std::string>& headers) {
auto req = GeneratePresignedUrlRequest(bucketName, keyName, MethodType::POST_METHOD);
// 设置过期时间为1小时
req.SetExpires(std::time(nullptr) + 3600);
req.SetParameters({{"uploads", ""}});
req.SetHeaders(headers);
return client.GeneratePresignedUrl(req);
}
using namespace ks3::sdk;
std::string generateUploadPartPreSignedUrl(const KS3Client& client, const std::string& bucketName, const std::string& keyName, const std::string& uploadId, int partNum) {
auto req = GeneratePresignedUrlRequest(bucketName, keyName, MethodType::PUT_METHOD);
// 设置过期时间为1小时
req.SetExpires(std::time(nullptr) + 3600);
req.SetParameters({{"partNumber", std::to_string(partNum)}, {"uploadId", uploadId}});
return client.GeneratePresignedUrl(req);
}
using namespace ks3::sdk;
std::string generateCompleteMultipartUploadPreSignedUrl(const KS3Client& client, const std::string& bucketName, const std::string& keyName, const std::string& uploadId) {
auto req = GeneratePresignedUrlRequest(bucketName, keyName, MethodType::POST_METHOD);
// 设置过期时间为1小时
req.SetExpires(std::time(nullptr) + 3600);
req.SetParameters({{"uploadId", uploadId}});
req.SetHeaders({{"Content-Type", "application/xml"}});
return client.GeneratePresignedUrl(req);
}
/**
* 初始化分块上传,并得到uploadId
*
* @param initUrl 初始化分块上传的预签名URL
* @param headers 请求头, 必须和生成预签名URL时的请求头一致
* @return uploadId 分块上传的uploadId, 后续环节生成预签名url时需要用到
*/
std::string initMultipartUploadByUrl(const std::string& initUrl, const std::map<std::string, std::string>& headers) {
Response resp = doRequest({0, 0, nullptr, headers, "POST", initUrl});
std::string uploadId;
// 解析响应XML,得到uploadId
xmlKeepBlanksDefault(0);
xmlDocPtr doc = xmlReadMemory(resp.body.c_str(), resp.body.size(), nullptr, "UTF-8", XML_PARSE_RECOVER);
if (doc != nullptr) {
if (xmlNodePtr proot = xmlDocGetRootElement(doc); proot != nullptr) {
xmlNodePtr cur = proot->xmlChildrenNode;
while (cur != nullptr) {
if (xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>("UploadId")) == 0) {
xmlChar* tmp_content = xmlNodeGetContent(cur);
uploadId.assign(reinterpret_cast<char*>(tmp_content));
xmlFree(tmp_content);
break;
}
cur = cur->next;
}
}
}
xmlFreeDoc(doc);
return uploadId;
}
std::map<std::string, std::string> uploadPartByUrl(const std::string& uploadUrl, const std::string& filename, int partNum, long partSize) {
long fileSize = getFileSize(filename);
long offset = (partNum - 1) * partSize;
long uploadSize = offset + partSize > fileSize ? fileSize - offset : partSize;
std::shared_ptr<std::iostream> content = std::make_shared<std::fstream>(filename, std::ios::in|std::ios::binary);
// 移动到指定offset
content->seekg(offset, std::ios::beg);
Response resp = doRequest({0, uploadSize, content, {}, "PUT", uploadUrl});
// 提取响应头中的信息,构造partInfo
auto etag = resp.headers["ETag"];
auto crc = resp.headers["x-kss-checksum-crc64ecma"];
std::map<std::string, std::string> partInfo = {
{"PartNumber", std::to_string(partNum)},
{"ETag", etag},
{"ChecksumCRC64ECMA", crc}
};
return partInfo;
}
void completeUploadByUrl(const std::string& completeUrl, const std::vector<std::map<std::string, std::string>>& partInfos) {
// 通过partInfos构造XML,作为请求体
xmlDocPtr doc = xmlNewDoc(nullptr);
xmlNodePtr root = xmlNewNode(nullptr, reinterpret_cast<const xmlChar*>("CompleteMultipartUpload"));
xmlDocSetRootElement(doc, root);
for (const auto& partInfo : partInfos) {
xmlNodePtr part = xmlNewNode(nullptr, reinterpret_cast<const xmlChar*>("Part"));
xmlNodePtr partNumber = xmlNewNode(nullptr, reinterpret_cast<const xmlChar*>("PartNumber"));
xmlNodePtr crc = xmlNewNode(nullptr, reinterpret_cast<const xmlChar*>("ChecksumCRC64ECMA"));
xmlNodePtr etag = xmlNewNode(nullptr, reinterpret_cast<const xmlChar*>("ETag"));
xmlNodeAddContent(partNumber, reinterpret_cast<const xmlChar*>(partInfo.at("PartNumber").c_str()));
xmlNodeAddContent(crc, reinterpret_cast<const xmlChar*>(partInfo.at("ChecksumCRC64ECMA").c_str()));
xmlNodeAddContent(etag, reinterpret_cast<const xmlChar*>(partInfo.at("ETag").c_str()));
xmlAddChild(part, partNumber);
xmlAddChild(part, crc);
xmlAddChild(part, etag);
xmlAddChild(root, part);
}
xmlChar* out = nullptr;
int len = 0;
xmlDocDumpFormatMemory(doc, &out, &len, 1);
std::string content;
content.assign(reinterpret_cast<char*>(out));
xmlFree(out);
xmlFreeDoc(doc);
xmlMemoryDump();
std::shared_ptr<std::stringstream> ss = std::make_shared<std::stringstream>(content);
const Response resp = doRequest({0, len, ss, {{"Content-Type", "application/xml"}}, "POST", completeUrl});
}
using namespace ks3::sdk;
void multiuploadByPreSignUrlDemo() {
std::string filename = "<your file path>";
std::string ak = "<your access key>";
std::string sk = "<your secret key>";
std::string host = "<your endpoint>"; // 填写桶所在region对应的Endpoint
// 初始化client
KS3Client client(host, ak, sk);
std::string bucketName = "<your bucket name>";
std::string keyName = "<your key name>";
// 获取初始化分块上传的预签名URL
// 设置请求头
std::map<std::string, std::string> headers = {{"Content-Type", "text/plain"}};
std::string initUrl = generateInitMultipartUploadPreSignedUrl(client, bucketName, keyName, headers);
// 使用预签名URL初始化分块上传,并得到uploadId
// 请求头必须和生成预签名URL时的请求头一致
auto uploadId = initMultipartUploadByUrl(initUrl, headers);
long partSize = 100 * 1024 * 1024;
long fileSize = getFileSize(filename);
int partCount = static_cast<int>((fileSize + partSize - 1) / partSize);
std::vector<std::map<std::string, std::string>> partInfos;
for (int partNum = 1; partNum <= partCount; partNum++) {
// 获取分块上传的预签名URL
std::string partUploadUrl = generateUploadPartPreSignedUrl(client, bucketName, keyName, uploadId, partNum);
// 使用预签名URL上传分块
auto partInfo = uploadPartByUrl(partUploadUrl, filename, partNum, partSize);
partInfos.push_back(partInfo);
}
// 获取完成分块上传的预签名URL
std::string completeUrl = generateCompleteMultipartUploadPreSignedUrl(client, bucketName, keyName, uploadId);
// 使用预签名URL完成分块上传
completeUploadByUrl(completeUrl, partInfos);
}
// 用于保存响应信息
struct Response {
int status_code;
std::string status_msg;
std::map<std::string, std::string> headers;
std::string body;
Response() : status_code(0) {}
};
// 用于保存请求信息
struct ResourceManager {
long send; // request body send
long total; // request body content-length
std::shared_ptr<std::iostream> body;
std::map<std::string, std::string> headers;
std::string method;
std::string url;
};
// 读取响应体的方法
size_t ReadResponseBody(void* buffer, size_t size, size_t nmemb, void* userdata) {
const size_t num_bytes = size * nmemb;
auto* res = static_cast<Response*>(userdata);
res->body.append(static_cast<char*>(buffer), num_bytes);
return num_bytes;
}
// 发送请求体的方法
size_t SendData(char* buffer, size_t size, size_t nmemb, void* userdata) {
auto* rm = static_cast<ResourceManager*>(userdata);
std::shared_ptr<std::iostream>& content = rm->body;
const size_t wanted = size * nmemb;
size_t got = 0;
if (content != nullptr && wanted > 0) {
size_t read = wanted;
if (rm->total > 0) {
int64_t remains = rm->total - rm->send;
if (remains < static_cast<int64_t>(wanted)) {
read = static_cast<size_t>(remains);
}
}
content->read(buffer, read);
got = static_cast<size_t>(content->gcount());
}
rm->send += got;
return got;
}
// 读取响应头的方法
size_t ReadHeaders(char* buffer, size_t size, size_t nmemb, void* userdata) {
const size_t num_bytes = size * nmemb;
if (num_bytes <= 0) {
return 0;
}
auto* res = static_cast<Response*>(userdata);
std::string line(buffer);
if (const auto key_len = line.find(':'); key_len != std::string::npos) {
auto value_len = line.rfind('\r');
if (value_len != std::string::npos) {
value_len = value_len - key_len - 2;
}
// header格式为:key: value\r
std::string key = line.substr(0, key_len);
std::string value = line.substr(key_len + 2, value_len);
res->headers.insert(std::pair(key, value));
} else if (line.find("HTTP") == 0) {
// 获取status_msg HTTP/1.1 200 OK\r\n
const auto first_space_pos = line.find(' ');
if (first_space_pos == std::string::npos) return num_bytes;
const auto second_space_pos = line.find(' ', first_space_pos + 1);
if (second_space_pos == std::string::npos) return num_bytes;
const auto end_pos = line.rfind('\r');
if (end_pos == std::string::npos) return num_bytes;
res->status_msg.append(line.substr(second_space_pos + 1, end_pos - second_space_pos - 1));
}
return num_bytes;
}
Response doRequest(const ResourceManager& rm) {
CURL* curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, rm.url.c_str());
if (rm.method == "POST") {
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, rm.total);
curl_easy_setopt(curl, CURLOPT_TRANSFER_ENCODING, 0L);
} else if (rm.method == "PUT") {
curl_easy_setopt(curl, CURLOPT_PUT, 1L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, rm.total);
}
// add headers
curl_slist* list = nullptr;
for (const auto& p : rm.headers) {
if (p.second.empty())
continue;
std::string str(p.first);
str.append(":").append(p.second);
list = curl_slist_append(list, str.c_str());
}
// Disable Expect: 100-continue
list = curl_slist_append(list, "Expect:");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
Response response;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ReadResponseBody);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, ReadHeaders);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, SendData);
curl_easy_setopt(curl, CURLOPT_READDATA, rm);
if(const CURLcode res = curl_easy_perform(curl); res != CURLE_OK)
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
else
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, & response.status_code);
return response;
}
long getFileSize(const std::string &filename) {
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file) {
std::cerr << "[ERROR] open file=" << filename << " failed" << std::endl;
return -1;
}
long len = file.tellg();
file.close();
return len;
}
纯净模式