diff --git a/demo/cos_demo.cpp b/demo/cos_demo.cpp index 164d2e1..8f8e0b8 100644 --- a/demo/cos_demo.cpp +++ b/demo/cos_demo.cpp @@ -839,6 +839,73 @@ void GetObjectACL(qcloud_cos::CosAPI& cos, const std::string& bucket_name, << std::endl; } +// 为已存在的 Object 设置标签(Tag) +void PutObjectTagging(qcloud_cos::CosAPI& cos, const std::string& bucket_name, + const std::string& object_name) { + qcloud_cos::PutObjectTaggingReq req(bucket_name, object_name); + qcloud_cos::PutObjectTaggingResp resp; + std::vector tagset; + Tag tag1; + tag1.SetKey("age"); + tag1.SetValue("19"); + + Tag tag2; + tag2.SetKey("name"); + tag2.SetValue("xiaoming"); + + Tag tag3; + tag3.SetKey("sex"); + tag3.SetValue("male"); + + tagset.push_back(tag1); + tagset.push_back(tag2); + tagset.push_back(tag3); + req.SetTagSet(tagset); + + qcloud_cos::CosResult result = cos.PutObjectTagging(req, &resp); + std::cout << "===================PutBucketTagging=====================" + << std::endl; + PrintResult(result, resp); + std::cout + << "====================================================================" + << std::endl; +} + +//查询指定Object的标签 +void GetObjectTagging(qcloud_cos::CosAPI& cos, const std::string& bucket_name, + const std::string& object_name) { + qcloud_cos::GetObjectTaggingReq req(bucket_name, object_name); + qcloud_cos::GetObjectTaggingResp resp; + + qcloud_cos::CosResult result = cos.GetObjectTagging(req, &resp); + std::cout << "===================GetObjectTagging=====================" + << std::endl; + std::vector tagset = resp.GetTagSet(); + for (std::vector::iterator it = tagset.begin(); it != tagset.end(); + ++it) { + std::cout << it->GetKey() << ":" << it->GetValue() << std::endl; + } + PrintResult(result, resp); + std::cout + << "====================================================================" + << std::endl; +} + +//删除指定Object的标签 +void DeleteObjectTagging(qcloud_cos::CosAPI& cos, const std::string& bucket_name, + const std::string& object_name) { + qcloud_cos::DeleteObjectTaggingReq req(bucket_name, object_name); + qcloud_cos::DeleteObjectTaggingResp resp; + + qcloud_cos::CosResult result = cos.DeleteObjectTagging(req, &resp); + std::cout << "===================DeleteObjectTagging=====================" + << std::endl; + PrintResult(result, resp); + std::cout + << "====================================================================" + << std::endl; +} + void PutObjectCopy(qcloud_cos::CosAPI& cos, const std::string& bucket_name, const std::string& object_name, const std::string& source) { qcloud_cos::PutObjectCopyReq req(bucket_name, object_name); diff --git a/include/cos_api.h b/include/cos_api.h index 1ed0042..8d95b41 100644 --- a/include/cos_api.h +++ b/include/cos_api.h @@ -539,6 +539,33 @@ class CosAPI { /// \return 本次请求的调用情况(如状态码等) CosResult PutObjectACL(const PutObjectACLReq& req, PutObjectACLResp* resp); + /// \brief 已存在的Object设置标签. + /// + /// \param req PutObjectTagging请求 + /// \param resp PutObjectTagging返回 + /// + /// \return 本次请求的调用情况(如状态码等) + CosResult PutObjectTagging(const PutObjectTaggingReq& req, + PutObjectTaggingResp* resp); + + /// \brief 查询指定对象下已有的对象标签. + /// + /// \param req GetObjectTagging请求 + /// \param resp GetObjectTagging返回 + /// + /// \return 本次请求的调用情况(如状态码等) + CosResult GetObjectTagging(const GetObjectTaggingReq& req, + GetObjectTaggingResp* resp); + + /// \brief 删除指定对象下已有的对象标签. + /// + /// \param req DeleteObjectTagging请求 + /// \param resp DeleteObjectTagging返回 + /// + /// \return 本次请求的调用情况(如状态码等) + CosResult DeleteObjectTagging(const DeleteObjectTaggingReq& req, + DeleteObjectTaggingResp* resp); + /// \brief 复制Object, 适用于跨园区且Object小于5G /// /// \param req PutObjectCopy请求 diff --git a/include/cos_params.h b/include/cos_params.h index b8d9511..9e879fe 100644 --- a/include/cos_params.h +++ b/include/cos_params.h @@ -62,6 +62,7 @@ const std::string kRespHeaderXCosReqId = "x-cos-request-id"; const std::string kRespHeaderXCosTraceId = "x-cos-trace-id"; const std::string kRespHeaderXCosNextAppendPosition = "x-cos-next-append-position"; const std::string kRespHeaderXCosContentSha1 = "x-cos-content-sha1"; +const std::string kRespHeaderXCosTaggingCount = "x-cos-tagging-count"; // doc preview response header const std::string kRespHeaderXTotalPage = "X-Total-Page"; diff --git a/include/op/object_op.h b/include/op/object_op.h index 98ae4b8..bd14a17 100644 --- a/include/op/object_op.h +++ b/include/op/object_op.h @@ -207,6 +207,33 @@ class ObjectOp : public BaseOp { /// \return 本次请求的调用情况(如状态码等) CosResult PutObjectACL(const PutObjectACLReq& req, PutObjectACLResp* resp); + /// \brief 已存在的Object设置标签. + /// + /// \param req PutObjectTagging请求 + /// \param resp PutObjectTagging返回 + /// + /// \return 本次请求的调用情况(如状态码等) + CosResult PutObjectTagging(const PutObjectTaggingReq& req, + PutObjectTaggingResp* resp); + + /// \brief 查询指定对象下已有的对象标签. + /// + /// \param req GetObjectTagging请求 + /// \param resp GetObjectTagging返回 + /// + /// \return 本次请求的调用情况(如状态码等) + CosResult GetObjectTagging(const GetObjectTaggingReq& req, + GetObjectTaggingResp* resp); + + /// \brief 删除指定对象下已有的对象标签. + /// + /// \param req DeleteObjectTagging请求 + /// \param resp DeleteObjectTagging返回 + /// + /// \return 本次请求的调用情况(如状态码等) + CosResult DeleteObjectTagging(const DeleteObjectTaggingReq& req, + DeleteObjectTaggingResp* resp); + /// \brief 复制Object /// /// \param req PutObjectCopy请求 diff --git a/include/request/object_req.h b/include/request/object_req.h index 65d2e98..a65a839 100644 --- a/include/request/object_req.h +++ b/include/request/object_req.h @@ -998,6 +998,69 @@ class PutObjectACLReq : public ObjectReq { std::vector m_acl; }; +class PutObjectTaggingReq : public ObjectReq { + public: + PutObjectTaggingReq(const std::string& bucket_name, const std::string& object_name) + : ObjectReq(bucket_name, object_name) { + SetMethod("PUT"); + AddParam("tagging", ""); + } + + void SetTagSet(std::vector& tagset) { m_tagset = tagset; } + + std::vector GetTagSet() { return m_tagset; } + + //清除tag规则. + void ClearTagSet() { + std::vector temp; + m_tagset.swap(temp); + } + + /// 添加单个tag. + void AddTag(const Tag& tag) { m_tagset.push_back(tag); } + + void SetVersionId(const std::string& str) { + AddParam("VersionId", str); + } + + bool GenerateRequestBody(std::string* body) const; + + virtual ~PutObjectTaggingReq() {} + + private: + std::vector m_tagset; +}; + +class GetObjectTaggingReq : public ObjectReq { + public: + GetObjectTaggingReq(const std::string& bucket_name, const std::string& object_name) + : ObjectReq(bucket_name, object_name) { + SetMethod("GET"); + AddParam("tagging", ""); + } + + void SetVersionId(const std::string& str) { + AddParam("VersionId", str); + } + + virtual ~GetObjectTaggingReq() {} +}; + +class DeleteObjectTaggingReq : public ObjectReq { + public: + DeleteObjectTaggingReq(const std::string& bucket_name, const std::string& object_name) + : ObjectReq(bucket_name, object_name) { + SetMethod("DELETE"); + AddParam("tagging", ""); + } + + void SetVersionId(const std::string& str) { + AddParam("VersionId", str); + } + + virtual ~DeleteObjectTaggingReq() {} +}; + class PutObjectCopyReq : public ObjectReq { public: PutObjectCopyReq(const std::string& bucket_name, diff --git a/include/response/base_resp.h b/include/response/base_resp.h index 537ff5f..d51942b 100644 --- a/include/response/base_resp.h +++ b/include/response/base_resp.h @@ -96,6 +96,9 @@ class BaseResp { std::string GetConnection() const { return GetHeader(kHttpHeaderConnection); } std::string GetDate() const { return GetHeader(kHttpHeaderDate); } std::string GetServer() const { return GetHeader(kHttpHeaderServer); } + std::string GetXCosTaggingCount() const { + return GetHeader(kRespHeaderXCosTaggingCount); + } std::string GetContentDisposition() const { return GetHeader(kHttpHeaderContentDisposition); } diff --git a/include/response/object_resp.h b/include/response/object_resp.h index a17af3e..c666238 100644 --- a/include/response/object_resp.h +++ b/include/response/object_resp.h @@ -350,6 +350,42 @@ class PutObjectACLResp : public BaseResp { virtual ~PutObjectACLResp() {} }; +class PutObjectTaggingResp : public BaseResp { + public: + PutObjectTaggingResp() {} + virtual ~PutObjectTaggingResp() {} +}; + +class GetObjectTaggingResp : public BaseResp { + public: + GetObjectTaggingResp() {} + + void SetTagSet(std::vector& tagset) { m_tagset = tagset; } + + std::vector GetTagSet() const { return m_tagset; } + + //清除tag规则. + void ClearTagSet() { + std::vector temp; + m_tagset.swap(temp); + } + + /// 添加单个tag. + void AddTag(const Tag& tag) { m_tagset.push_back(tag); } + + virtual bool ParseFromXmlString(const std::string& body); + virtual ~GetObjectTaggingResp() {} + + private: + std::vector m_tagset; +}; + +class DeleteObjectTaggingResp : public BaseResp { + public: + DeleteObjectTaggingResp() {} + virtual ~DeleteObjectTaggingResp() {} +}; + class PutObjectCopyResp : public BaseResp { public: PutObjectCopyResp() {} diff --git a/src/cos_api.cpp b/src/cos_api.cpp index e559464..43ba486 100644 --- a/src/cos_api.cpp +++ b/src/cos_api.cpp @@ -399,6 +399,22 @@ CosResult CosAPI::PutObjectACL(const PutObjectACLReq& req, return m_object_op.PutObjectACL(req, resp); } +CosResult CosAPI::PutObjectTagging(const PutObjectTaggingReq& req, + PutObjectTaggingResp* resp) { + return m_object_op.PutObjectTagging(req, resp); +} + +CosResult CosAPI::GetObjectTagging(const GetObjectTaggingReq& req, + GetObjectTaggingResp* resp) { + return m_object_op.GetObjectTagging(req, resp); +} + + +CosResult CosAPI::DeleteObjectTagging(const DeleteObjectTaggingReq& req, + DeleteObjectTaggingResp* resp) { + return m_object_op.DeleteObjectTagging(req, resp); +} + CosResult CosAPI::PutObjectCopy(const PutObjectCopyReq& req, PutObjectCopyResp* resp) { return m_object_op.PutObjectCopy(req, resp); diff --git a/src/op/object_op.cpp b/src/op/object_op.cpp index 067b939..5b84eee 100644 --- a/src/op/object_op.cpp +++ b/src/op/object_op.cpp @@ -868,6 +868,46 @@ CosResult ObjectOp::PutObjectACL(const PutObjectACLReq& req, req_body, false, resp); } +CosResult ObjectOp::PutObjectTagging(const PutObjectTaggingReq& req, + PutObjectTaggingResp* resp) { + std::string host = CosSysConfig::GetHost(GetAppId(), m_config->GetRegion(), + req.GetBucketName()); + std::string path = req.GetPath(); + + std::string req_body; + if (!req.GenerateRequestBody(&req_body)) { + CosResult result; + result.SetErrorMsg("Generate PutBucketWebsite Request Body fail."); + return result; + } + + std::string raw_md5 = CodecUtil::Base64Encode(CodecUtil::RawMd5(req_body)); + + std::map additional_headers; + std::map additional_params; + additional_headers.insert(std::make_pair("Content-MD5", raw_md5)); + return NormalAction(host, path, req, additional_headers, additional_params, + req_body, false, resp); +} + +CosResult ObjectOp::GetObjectTagging(const GetObjectTaggingReq& req, + GetObjectTaggingResp* resp) { + std::string host = CosSysConfig::GetHost(GetAppId(), m_config->GetRegion(), + req.GetBucketName()); + std::string path = req.GetPath(); + + return NormalAction(host, path, req, "", false, resp); +} + +CosResult ObjectOp::DeleteObjectTagging(const DeleteObjectTaggingReq& req, + DeleteObjectTaggingResp* resp) { + std::string host = CosSysConfig::GetHost(GetAppId(), m_config->GetRegion(), + req.GetBucketName()); + std::string path = req.GetPath(); + + return NormalAction(host, path, req, "", false, resp); +} + CosResult ObjectOp::PutObjectCopy(const PutObjectCopyReq& req, PutObjectCopyResp* resp) { std::string host = CosSysConfig::GetHost(GetAppId(), m_config->GetRegion(), diff --git a/src/request/object_req.cpp b/src/request/object_req.cpp index 5a8d9a3..9ac6da2 100644 --- a/src/request/object_req.cpp +++ b/src/request/object_req.cpp @@ -57,6 +57,41 @@ bool PutObjectACLReq::GenerateRequestBody(std::string* body) const { return GenerateAclRequestBody(m_owner, m_acl, body); } +// 设置标签集合. +bool PutObjectTaggingReq::GenerateRequestBody(std::string* body) const { + rapidxml::xml_document<> doc; + rapidxml::xml_node<>* root_node = doc.allocate_node( + rapidxml::node_element, doc.allocate_string("Tagging"), NULL); + doc.append_node(root_node); + rapidxml::xml_node<>* TagSet_node = doc.allocate_node( + rapidxml::node_element, doc.allocate_string("TagSet"), NULL); + + for (std::vector::const_iterator c_itr = m_tagset.begin(); + c_itr != m_tagset.end(); ++c_itr) { + rapidxml::xml_node<>* tag_node = doc.allocate_node( + rapidxml::node_element, doc.allocate_string("Tag"), NULL); + if (c_itr->HasKey()) { + tag_node->append_node( + doc.allocate_node(rapidxml::node_element, doc.allocate_string("Key"), + doc.allocate_string(c_itr->GetKey().c_str()))); + } else { + SDK_LOG_ERR("PutBucketTagging need to set Key."); + } + if (c_itr->HasValue()) { + tag_node->append_node(doc.allocate_node( + rapidxml::node_element, doc.allocate_string("Value"), + doc.allocate_string(c_itr->GetValue().c_str()))); + } else { + SDK_LOG_ERR("PutBucketTagging need to set Value."); + } + TagSet_node->append_node(tag_node); + } + root_node->append_node(TagSet_node); + rapidxml::print(std::back_inserter(*body), doc, 0); + doc.clear(); + return true; +} + std::map CopyReq::GetInitHeader() const { std::map init_headers; diff --git a/src/response/object_resp.cpp b/src/response/object_resp.cpp index 553394c..d84da51 100644 --- a/src/response/object_resp.cpp +++ b/src/response/object_resp.cpp @@ -285,6 +285,56 @@ bool GetObjectACLResp::ParseFromXmlString(const std::string& body) { &m_acl); } +// 解析GetObjectTagging Response +bool GetObjectTaggingResp::ParseFromXmlString(const std::string& body) { + std::string tmp_body = body; + rapidxml::xml_document<> doc; + + if (!StringUtil::StringToXml(&tmp_body[0], &doc)) { + SDK_LOG_ERR("Parse string to xml doc error, xml_body=%s", body.c_str()); + return false; + } + + rapidxml::xml_node<>* root = doc.first_node("Tagging"); + if (NULL == root) { + SDK_LOG_ERR("Miss root node = Tagging, xml_body=%s", body.c_str()); + return false; + } + + rapidxml::xml_node<>* TagSet_node = root->first_node("TagSet"); + if (NULL == TagSet_node) { + SDK_LOG_WARN("Miss node = TagSet, xml_body=%s", body.c_str()); + return false; + } + + rapidxml::xml_node<>* tag_node = TagSet_node->first_node(); + for (; tag_node != NULL; tag_node = tag_node->next_sibling()) { + const std::string& tage_node_name = tag_node->name(); + if (tage_node_name == "Tag") { + Tag temp_tag; + rapidxml::xml_node<>* node = tag_node->first_node(); + for (; node != NULL; node = node->next_sibling()) { + const std::string& node_name = node->name(); + if (node_name == "Key") { + std::string key(node->value()); + temp_tag.SetKey(key); + } else if (node_name == "Value") { + std::string value(node->value()); + temp_tag.SetValue(value); + } else { + continue; + SDK_LOG_WARN("Unknown field, field_name=%s, xml_body=%s", + node_name.c_str(), body.c_str()); + } + } + AddTag(temp_tag); + } else { + continue; + } + } + return true; +} + bool PutObjectCopyResp::ParseFromXmlString(const std::string& body) { rapidxml::xml_document<> doc; char* cstr = new char[body.size() + 1]; diff --git a/unittest/src/object_op_test.cpp b/unittest/src/object_op_test.cpp index b37bab6..ca02833 100644 --- a/unittest/src/object_op_test.cpp +++ b/unittest/src/object_op_test.cpp @@ -334,7 +334,95 @@ TEST_F(ObjectOpTest, PutObjectByFileTest) { ASSERT_TRUE(result.IsSucc()); } } +TEST_F(ObjectOpTest, ObjectTaggingTest){ + //上传文件 + std::string object_tagging_test_name = "ObjectTaggingTest"; + { + std::istringstream iss("put_obj_by_stream_with_test_obj_tagging"); + PutObjectByStreamReq req(m_bucket_name, object_tagging_test_name, iss); + req.SetXCosStorageClass(kStorageClassStandard); + PutObjectByStreamResp resp; + CosResult result = m_client->PutObject(req, &resp); + ASSERT_TRUE(result.IsSucc()); + } + //HeadObject + { + qcloud_cos::HeadObjectReq req(m_bucket_name, object_tagging_test_name); + qcloud_cos::HeadObjectResp resp; + qcloud_cos::CosResult result = m_client->HeadObject(req, &resp); + ASSERT_TRUE(result.IsSucc()); + EXPECT_EQ("", resp.GetXCosTaggingCount()); + } + //PutObjectTagging + { + qcloud_cos::PutObjectTaggingReq req(m_bucket_name, object_tagging_test_name); + qcloud_cos::PutObjectTaggingResp resp; + std::vector tagset; + Tag tag1; + tag1.SetKey("age"); + tag1.SetValue("19"); + + Tag tag2; + tag2.SetKey("name"); + tag2.SetValue("xiaoming"); + + Tag tag3; + tag3.SetKey("sex"); + tag3.SetValue("male"); + + tagset.push_back(tag1); + tagset.push_back(tag2); + tagset.push_back(tag3); + req.SetTagSet(tagset); + + qcloud_cos::CosResult result = m_client->PutObjectTagging(req, &resp); + EXPECT_TRUE(result.IsSucc()); + } + //GetObjectTagging + { + qcloud_cos::GetObjectTaggingReq req(m_bucket_name, object_tagging_test_name); + qcloud_cos::GetObjectTaggingResp resp; + qcloud_cos::CosResult result = m_client->GetObjectTagging(req, &resp); + EXPECT_TRUE(result.IsSucc()); + + std::vector tagSet = resp.GetTagSet(); + std::vector::iterator it = tagSet.begin(); + + EXPECT_EQ("age", it->GetKey()); + EXPECT_EQ("19", it->GetValue()); + ++it; + EXPECT_EQ("name", it->GetKey()); + EXPECT_EQ("xiaoming", it->GetValue()); + ++it; + EXPECT_EQ("sex", it->GetKey()); + EXPECT_EQ("male", it->GetValue()); + } + //HeadObject + { + qcloud_cos::HeadObjectReq req(m_bucket_name, object_tagging_test_name); + qcloud_cos::HeadObjectResp resp; + qcloud_cos::CosResult result = m_client->HeadObject(req, &resp); + ASSERT_TRUE(result.IsSucc()); + EXPECT_EQ("3", resp.GetXCosTaggingCount()); + } + //DeleteObjectTagging + { + qcloud_cos::DeleteObjectTaggingReq req(m_bucket_name, object_tagging_test_name); + qcloud_cos::DeleteObjectTaggingResp resp; + + qcloud_cos::CosResult result = m_client->DeleteObjectTagging(req, &resp); + ASSERT_TRUE(result.IsSucc()); + } + //HeadObject + { + qcloud_cos::HeadObjectReq req(m_bucket_name, object_tagging_test_name); + qcloud_cos::HeadObjectResp resp; + qcloud_cos::CosResult result = m_client->HeadObject(req, &resp); + ASSERT_TRUE(result.IsSucc()); + EXPECT_EQ("", resp.GetXCosTaggingCount()); + } +} TEST_F(ObjectOpTest, PutObjectByStreamTest) { // 1. ObjectName为普通字符串 {