如何在Azure Blob容器中删除文件夹

62

我在Azure中有一个名为pictures的blob容器,其中包含各种文件夹(请参见下面的快照):

enter image description here

我正在尝试删除快照中显示的标题为usersuploads的文件夹,但无法删除,出现错误:Failed to delete blob pictures/uploads/. Error: The specified blob does not exist. 有谁能解释一下我如何删除这两个文件夹吗?我通过谷歌搜索没有发现有意义的信息。

注意:如果需要,可以向我询问更多信息。


1
请尝试使用此客户端 https://azurestorageexplorer.codeplex.com/ - Mariano Montañez Ureta
15个回答

63

Windows Azure Blob Storage没有文件夹的概念。层次结构非常简单:存储帐户>容器>Blob。事实上,删除特定的文件夹就是删除以该文件夹名称开头的所有Blob。您可以编写如下简单代码来删除您的文件夹:

CloudStorageAccount storageAccount = CloudStorageAccount.Parse("your storage account");
CloudBlobContainer container = storageAccount.CreateCloudBlobClient().GetContainerReference("pictures");
foreach (IListBlobItem blob in container.GetDirectoryReference("users").ListBlobs(true))
{
    if (blob.GetType() == typeof(CloudBlob) || blob.GetType().BaseType == typeof(CloudBlob))
    {
        ((CloudBlob)blob).DeleteIfExists();
    }
}

container.GetDirectoryReference("users").ListBlobs(true)列出了“picture”容器中以“users”开头的所有Blob,您随后可以逐个删除它们。要删除其他文件夹,只需像这样指定GetDirectoryReference("您的文件夹名称")


3
没错,删除文件夹内的 Blob 也会删除文件夹本身。我们只需删除文件夹内的所有 Blob 即可。 - Malatesh Patil
它是否也会删除一个名为users2的文件夹? - Sunil Kumar
1
对于那些寻找最新的(截至2021年)使用Azure.Storage.Blobs v12的人,请查看下面@Melih的其他答案:https://dev59.com/_lsW5IYBdhLWcg3wqYp7#63846242 - Thiago Silva
为什么可以使用Python或Azure Storage Explorer在Azure存储Blob容器中创建“文件夹”,即使为空,该文件夹也会持久存在?为什么我可以填充此“/folder001/”,然后将这些文件移动到另一个“/folder002/”,并且我清楚地看到现在空的/folder001/继续持久存在,直到我手动删除它?SME们对Azure存储Blob容器中的“文件夹”有什么不理解的地方? - Mark Vogt
如果您已禁用分层命名空间,则此内容为真。如果已启用,则删除文件夹中的唯一 Blob 后,文件夹将不会被删除。 - Jorn.Beyers
显示剩余4条评论

29

这确实可以避免点击/脚本,但内部仍使用相同的枚举-逐个删除机制。因此,它可能会执行大量的删除操作,速度非常慢。 - Imre Pühvel
可能会慢一些,但它有一个“将AzCopy命令复制到剪贴板”的功能,向我展示了如何在命令行上完成此操作 :) - fuenfundachtzig
已添加答案,展示如何直接使用azcopy(速度较快)完成此操作。 - fuenfundachtzig

23

在最新的代码库中,Azure.Storage.Blobs非常简单明了。

var connectionString = "blob-connection-string";
var containerName = "container-name";
var folderPath = "folder1/subfolder/sub-subfolder";

var blobServiceClient = new BlobServiceClient(connectionString);
var blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName);
var blobItems = blobContainerClient.GetBlobsAsync(prefix: folderPath);
await foreach (BlobItem blobItem in blobItems)
{
     BlobClient blobClient = blobContainerClient.GetBlobClient(blobItem.Name);
     await blobClient.DeleteIfExistsAsync();
}

每个 Blob 都有其自己的 URI 值,您可以在查询之前设置前缀,以便它可以获取和删除特定 URI 的 Blob。随着 Blob 被删除,文件夹也会消失。


我可以知道如何执行那个脚本吗? - adey27
除非:您无法删除非空文件夹,也无法删除空文件夹。我喜欢这些错误消息。 - klenium

16

让我们以一个例子来开始,介绍如何使用ListBlobsSegmentedAsyc删除“文件夹”:

var container = // get container reference
var ctoken = new BlobContinuationToken();
do
{
    var result = await container.ListBlobsSegmentedAsync("myfolder", true, BlobListingDetails.None, null, ctoken, null, null);
    ctoken = result.ContinuationToken;
    await Task.WhenAll(result.Results
        .Select(item => (item as CloudBlob)?.DeleteIfExistsAsync())
        .Where(task => task != null)
    );
} while (ctoken != null);

它的作用是什么...

var ctoken = new BlobContinuationToken();

一个"文件夹"可能包含大量的文件。ListBlobSegmentedAsyc可能会只返回其中的一部分。这个令牌将存储信息,以便在下一次调用中继续。

var result = await container.ListBlobsSegmentedAsync("myfolder", true, BlobListingDetails.None, null, ctoken, null, null);
  • 第一个参数是所需的 Blob 名称("path")前缀。
  • 第二个参数"useFlatBlobListing=true"告诉客户端返回所有子文件夹中的所有项目。如果设置为 false,则运行在"虚拟文件夹"模式下,类似于文件系统。
  • ctoken 将告诉 Azure 在哪里继续。

有关所有参数,请参见https://learn.microsoft.com/en-us/dotnet/api/microsoft.windowsazure.storage.blob.cloudblobclient.listblobssegmentedasync?view=azure-dotnet获取详细信息。

(item as CloudBlob)?.DeleteIfExistsAsync()

现在我们有一个IListBlobItem类型的列表在result.Results中。因为不能保证IListBlobItem是可删除的CloudBlob(例如,如果我们设置useFlatBlobListing=false它可能是一个虚拟文件夹),所以我们尝试进行类型转换并删除它(如果可能)。

result.Results.Select(item => (item as CloudBlob)?.DeleteIfExistsAsync())

激活删除所有结果并返回任务列表。

.Where(task => task != null)

如果结果包含我们无法转换为CloudBlob的项目,我们的任务列表将包含空值。我们必须删除它们。

... 然后我们等待当前段的所有删除完成,如果有下一段则继续进行。


10
因为“文件夹”实际上不存在。在Azure存储帐户中,您有包含Blob的容器。客户端所看到的“文件夹”是帐户“pictures/uploads/”中Blob的文件名。如果要删除“文件夹”,实际上必须删除使用相同“路径”命名的每个Blob。 最常见的方法是获取这些Blob的列表,然后将其提供给删除Blob调用。

1
好的,但是删除 blob 的调用能够扩展到例如 2000 万个对象吗? - Hassan Baig
1
“规模”是一个相对的术语。你能删除所有20M的blob吗?可以,但你不能在1秒内完成。你将受到存储API限制的影响。 - BrentDaCodeMonkey
1
不过加一个“全选”复选框也无妨。 - Guillaume

5
自版本9.4.0起,WindowsAzure.Storage包已分为多个单独的包。这意味着在较新的Azure.Storage.Blobs包中使用的API已发生更改,与所接受的答案中使用的API不同。
下面的方法使用较新的Azure.Storage.Blobs包中的API,但仍然使用所接受的答案的相同方法,即列出所有blob,然后逐个删除它们。
string ConnectionString = "<your connection string>";
string ContainerName = "<your container name>";

private BlobContainerClient ContainerClient()
{
    var client = new BlobContainerClient(ConnectionString, ContainerName);
    client.CreateIfNotExists();
    return client;
}

public async Task<List<BlobItem>> ListBlobsAsync(string folder)
{
    var c = ContainerClient();
    var enumerator = c.GetBlobsByHierarchyAsync(prefix: folder).GetAsyncEnumerator();

    var result = new List<BlobItem>();
    while (await enumerator.MoveNextAsync())
    {
        if (enumerator.Current.IsBlob)
            result.Add(enumerator.Current.Blob);
    }
    return result;
}

public async Task DeleteByFolderAsync(string folder)
{
    var c = ContainerClient();
    foreach (var blob in await ListBlobsAsync(folder))
    {
        await c.GetBlobClient(blob.Name).DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots);
    }
}

完美!正是我想要的。 - irhetoric

4

尝试使用Azure CLI

例如,如果您想删除以pictures/users开头的路径,这里可以找到所有的 blobs。

export CONN_STRING="<YOUR-CONNECTION-STRING>"

az storage blob list -c mycontainer \
   --connection-string $CONN_STRING \
   --output tsv \
   --prefix pictures/users

或者您可能想直接删除它们:

az storage blob delete-batch -s mycontainer \
   --connection-string $CONN_STRING \
   --pattern pictures/users/*

2

一些简单的代码可以实现所需的行为:

    public static async Task DeleteFolder(string containerName, string folder)
    {
        CloudBlobContainer container = await GetContainerAsync(containerName);

        BlobResultSegment blobList = null;
        bool folderIsEmpty = false;

        while (!folderIsEmpty)
        {
            blobList = await container.ListBlobsSegmentedAsync(
                prefix: folder,
                useFlatBlobListing: true,
                blobListingDetails: BlobListingDetails.None,
                maxResults: null,
                currentToken: null,
                options: null,
                operationContext: null
            );

            folderIsEmpty = true;

            foreach (IListBlobItem item in blobList.Results)
            {
                folderIsEmpty = false;
                await ((CloudBlockBlob)item).DeleteIfExistsAsync();
            }
        }
    }

1
这就是我要找的答案。 - sandeep.gosavi

2
使用 Azure CLI,使用命令 delete-batch 可以递归删除目录中的所有 Blob,但不会删除目录本身。
正如之前所述,层次结构为存储帐户 > 容器 > Blob。因此,您可以将目录视为 Blob 并使用 azcopy 命令:
az storage azcopy blob delete --account-name MyAccount --container MyContainer --target MyDirectory 

2

您也可以在Azure云shell中执行此操作。以下是命令:

az storage blob delete-batch --source <blob-container> --account-name <blob-account> --pattern <folder-name>*

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接