[Storage Blobs Service] ListBlobsByHierarchy (#265)
* add API ListBlobsByHierarchy * fix compiler error on GCC 8 * Update Blob API version to 2019-12-12 * use lowercase header key
This commit is contained in:
parent
0eacb230cb
commit
3db59439c9
@ -176,9 +176,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
* @brief Returns a single segment of blobs in this container, starting from the
|
||||
* specified Marker, Use an empty Marker to start enumeration from the beginning and the
|
||||
* NextMarker if it's not empty to make subsequent calls to ListBlobsFlat to continue
|
||||
* enumerating the blobs segment by segment. Blobs are ordered lexicographically by name. A
|
||||
* Delimiter can be used to traverse a virtual hierarchy of blobs as though it were a file
|
||||
* system.
|
||||
* enumerating the blobs segment by segment. Blobs are ordered lexicographically by name.
|
||||
*
|
||||
* @param options Optional parameters to execute this function.
|
||||
* @return A
|
||||
@ -186,6 +184,23 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
*/
|
||||
BlobsFlatSegment ListBlobsFlat(const ListBlobsOptions& options = ListBlobsOptions()) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a single segment of blobs in this container, starting from the
|
||||
* specified Marker, Use an empty Marker to start enumeration from the beginning and the
|
||||
* NextMarker if it's not empty to make subsequent calls to ListBlobsByHierarchy to continue
|
||||
* enumerating the blobs segment by segment. Blobs are ordered lexicographically by name. A
|
||||
* Delimiter can be used to traverse a virtual hierarchy of blobs as though it were a file
|
||||
* system.
|
||||
*
|
||||
* @param delimiter This can be used to to traverse a virtual hierarchy of blobs as though it
|
||||
* were a file system. The delimiter may be a single character or a string.
|
||||
* @param options Optional parameters to execute this function.
|
||||
* @return A BlobsFlatSegment describing a segment of the blobs in the container.
|
||||
*/
|
||||
BlobsHierarchySegment ListBlobsByHierarchy(
|
||||
const std::string& delimiter,
|
||||
const ListBlobsOptions& options = ListBlobsOptions()) const;
|
||||
|
||||
private:
|
||||
UrlBuilder m_containerUrl;
|
||||
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;
|
||||
|
||||
@ -241,12 +241,6 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
*/
|
||||
Azure::Core::Nullable<std::string> Prefix;
|
||||
|
||||
/**
|
||||
* @brief Used to traverse a virtual hierarchy of blobs as though it were a file
|
||||
* system.
|
||||
*/
|
||||
Azure::Core::Nullable<std::string> Delimiter;
|
||||
|
||||
/**
|
||||
* @brief A string value that identifies the portion of the list of blobs to be
|
||||
* returned with the next listing operation. The operation returns a non-empty
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -166,13 +166,26 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
|
||||
BlobsFlatSegment BlobContainerClient::ListBlobsFlat(const ListBlobsOptions& options) const
|
||||
{
|
||||
BlobRestClient::Container::ListBlobsOptions protocolLayerOptions;
|
||||
BlobRestClient::Container::ListBlobsFlatOptions protocolLayerOptions;
|
||||
protocolLayerOptions.Prefix = options.Prefix;
|
||||
protocolLayerOptions.Delimiter = options.Delimiter;
|
||||
protocolLayerOptions.Marker = options.Marker;
|
||||
protocolLayerOptions.MaxResults = options.MaxResults;
|
||||
protocolLayerOptions.Include = options.Include;
|
||||
return BlobRestClient::Container::ListBlobs(
|
||||
return BlobRestClient::Container::ListBlobsFlat(
|
||||
options.Context, *m_pipeline, m_containerUrl.ToString(), protocolLayerOptions);
|
||||
}
|
||||
|
||||
BlobsHierarchySegment BlobContainerClient::ListBlobsByHierarchy(
|
||||
const std::string& delimiter,
|
||||
const ListBlobsOptions& options) const
|
||||
{
|
||||
BlobRestClient::Container::ListBlobsByHierarchyOptions protocolLayerOptions;
|
||||
protocolLayerOptions.Prefix = options.Prefix;
|
||||
protocolLayerOptions.Delimiter = delimiter;
|
||||
protocolLayerOptions.Marker = options.Marker;
|
||||
protocolLayerOptions.MaxResults = options.MaxResults;
|
||||
protocolLayerOptions.Include = options.Include;
|
||||
return BlobRestClient::Container::ListBlobsByHierarchy(
|
||||
options.Context, *m_pipeline, m_containerUrl.ToString(), protocolLayerOptions);
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ namespace Azure { namespace Storage {
|
||||
"If-Unmodified-Since",
|
||||
"Range"})
|
||||
{
|
||||
auto ite = headers.find(headerName);
|
||||
auto ite = headers.find(Azure::Core::Details::ToLower(headerName));
|
||||
if (ite != headers.end())
|
||||
{
|
||||
if (headerName == "Content-Length" && ite->second == "0")
|
||||
|
||||
@ -146,48 +146,70 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
{
|
||||
const std::string delimiter = "/";
|
||||
const std::string prefix = RandomString();
|
||||
const std::string prefix1 = prefix + "-" + RandomString();
|
||||
const std::string prefix2 = prefix + "-" + RandomString();
|
||||
std::set<std::string> blobs;
|
||||
std::string blobName = prefix;
|
||||
for (int i = 0; i < 5; ++i)
|
||||
for (const auto& blobNamePrefix : {prefix1, prefix2})
|
||||
{
|
||||
blobName = blobName + delimiter + RandomString();
|
||||
auto blobClient = m_blobContainerClient->GetBlockBlobClient(blobName);
|
||||
auto emptyContent = Azure::Core::Http::MemoryBodyStream(nullptr, 0);
|
||||
blobClient.Upload(emptyContent);
|
||||
blobs.insert(blobName);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
std::string blobName = blobNamePrefix + delimiter + RandomString();
|
||||
auto blobClient = m_blobContainerClient->GetBlockBlobClient(blobName);
|
||||
auto emptyContent = Azure::Core::Http::MemoryBodyStream(nullptr, 0);
|
||||
blobClient.Upload(emptyContent);
|
||||
blobs.insert(blobName);
|
||||
}
|
||||
}
|
||||
|
||||
Azure::Storage::Blobs::ListBlobsOptions options;
|
||||
options.Delimiter = delimiter;
|
||||
options.Prefix = prefix + delimiter;
|
||||
std::set<std::string> listBlobs;
|
||||
options.Prefix = prefix;
|
||||
std::set<std::string> items;
|
||||
while (true)
|
||||
{
|
||||
auto res = m_blobContainerClient->ListBlobsFlat(options);
|
||||
EXPECT_EQ(res.Delimiter, options.Delimiter.GetValue());
|
||||
auto res = m_blobContainerClient->ListBlobsByHierarchy(delimiter, options);
|
||||
EXPECT_EQ(res.Delimiter, delimiter);
|
||||
EXPECT_EQ(res.Prefix, options.Prefix.GetValue());
|
||||
for (const auto& blob : res.Items)
|
||||
EXPECT_TRUE(res.Items.empty());
|
||||
for (const auto& i : res.BlobPrefixes)
|
||||
{
|
||||
listBlobs.insert(blob.Name);
|
||||
items.emplace(i.Name);
|
||||
}
|
||||
if (!res.NextMarker.empty())
|
||||
{
|
||||
options.Marker = res.NextMarker;
|
||||
}
|
||||
else if (!res.Items.empty())
|
||||
{
|
||||
options.Prefix = res.Items[0].Name + delimiter;
|
||||
if (options.Marker.HasValue())
|
||||
{
|
||||
options.Marker.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(listBlobs, blobs);
|
||||
EXPECT_EQ(items, (std::set<std::string>{prefix1 + delimiter, prefix2 + delimiter}));
|
||||
|
||||
items.clear();
|
||||
for (const auto& p : {prefix1, prefix2})
|
||||
{
|
||||
options.Prefix = p + delimiter;
|
||||
while (true)
|
||||
{
|
||||
auto res = m_blobContainerClient->ListBlobsByHierarchy(delimiter, options);
|
||||
EXPECT_EQ(res.Delimiter, delimiter);
|
||||
EXPECT_EQ(res.Prefix, options.Prefix.GetValue());
|
||||
EXPECT_TRUE(res.BlobPrefixes.empty());
|
||||
for (const auto& i : res.Items)
|
||||
{
|
||||
items.emplace(i.Name);
|
||||
}
|
||||
if (!res.NextMarker.empty())
|
||||
{
|
||||
options.Marker = res.NextMarker;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(items, blobs);
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user