How to delete a folder within an Azure blob container

AzureAzure StorageAzure Blob-Storage

Azure Problem Overview


I have a blob container in Azure called pictures that has various folders within it (see snapshot below):

enter image description here

I'm trying to delete the folders titled users and uploads shown in the snapshot, but I keep the error: Failed to delete blob pictures/uploads/. Error: The specified blob does not exist. Could anyone shed light on how I can delete those two folders? I haven't been able to uncover anything meaningful via Googling this issue.

Note: ask me for more information in case you need it

Azure Solutions


Solution 1 - Azure

Windows Azure Blob Storage does not have the concept of folders. The hierarchy is very simple: storage account > container > blob. In fact, removing a particular folder is removing all the blobs which start with the folder name. You can write the simple code as below to delete your folders:

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) lists the blobs start with "users" within the "picture" container, you can then delete them individually. To delete other folders, you just need to specify like this GetDirectoryReference("your folder name").

Solution 2 - Azure

There is also a desktop storage explorer from Microsoft. It has a feature where you can select the virtual folder and then delete it effectively deleting all sub blobs.

https://azure.microsoft.com/en-us/features/storage-explorer/

Solution 3 - Azure

Let's start with an example how to delete a "folder" using 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);

What it does...

var ctoken = new BlobContinuationToken();

A "folder" may contain a lot of files. ListBlobSegmentedAsyc may return only a part of them. This token will store the info where to continue in next call.

var result = await container.ListBlobsSegmentedAsync("myfolder", true, BlobListingDetails.None, null, ctoken, null, null);
  • First argument is the required blob name ("path") prefix.
  • Second argument "useFlatBlobListing=true" tells the client to return all items in all sub folders. If set to false, it will run in "virtual folders" mode and behave like a file system.
  • The ctoken will tell azure where to continue

For all arguments see https://docs.microsoft.com/en-us/dotnet/api/microsoft.windowsazure.storage.blob.cloudblobclient.listblobssegmentedasync?view=azure-dotnet for details.

(item as CloudBlob)?.DeleteIfExistsAsync()

Now we have a list of IListBlobItem in result.Results. Because an IListBlobItem is not guaranteed to be a deletable CloudBlob (e.g. it could be a virtual folder if we would have set useFlatBlobListing=false), we try to cast it and delete it if possible.

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

Triggers delete for all results and returns a list of tasks.

.Where(task => task != null)

If Results contained items we could not cast to CloudBlob, our list of tasks contains null values. We have to remove them.

... then we wait until all delete for current segment finished and continue with next segment if available.

Solution 4 - Azure

In the latest repo, Azure.Storage.Blobs, it's very straightforward

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();
}

As every blob has its own uri value, you can set prefix before querying so that it can fetch and delete blobs with the particular uri. The folder will disappear as the blobs get deleted.

Solution 5 - Azure

Its because the "folders" don't actually exist. In an Azure storage account, you have containers which are filled with blobs. What you see visualized by clients as "folders" are the file names of the blobs in the account "pictures/uploads/". If you want to remove the "folder", you actually have to remove each of the blobs that are named with the same "path".

The most common approach is to get a list of these blobs then feed that to the delete blob call.

Solution 6 - Azure

The WindowsAzure.Storage package was split into separate packages at version 9.4.0. This means that the APIs used in the accepted answer have changed in the more recent Azure.Storage.Blobs package.

The approach below uses the APIs from the newer Azure.Storage.Blobs package, but still uses the same approach of accepted answer by listing all the blobs and then deleting them one at a time.

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);
	}
}

Solution 7 - Azure

Try using the Azure CLI

For example if you want delete a path starting with pictures/users here you can find all the blobs

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

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

Or maybe you want go straight and delete all of them:

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

Solution 8 - Azure

You can also do it in Azure cloud shell; here is the command:

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

Solution 9 - Azure

Now you can use lifecycle management to delete file with prefixMatch and action to delete with property daysAfterModificationGreaterThan. Leave the rule active for about 24 hours. And it will do the job. Documentation of lifecycle management available at https://docs.microsoft.com/en-us/azure/storage/blobs/storage-lifecycle-management-concepts

Solution 10 - Azure

Some simple code to achieve the desired behaviour:

    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();
            }
        }
    }

Solution 11 - Azure

enter image description here You can now use the delete activity in ADF to delete any file /blob. It will delete all the files inside

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHassan BaigView Question on Stackoverflow
Solution 1 - Azureforester123View Answer on Stackoverflow
Solution 2 - AzurestueynetView Answer on Stackoverflow
Solution 3 - AzureChristoph LütjenView Answer on Stackoverflow
Solution 4 - AzureMelihView Answer on Stackoverflow
Solution 5 - AzureBrentDaCodeMonkeyView Answer on Stackoverflow
Solution 6 - AzureSean KearonView Answer on Stackoverflow
Solution 7 - AzurefreedevView Answer on Stackoverflow
Solution 8 - AzureRichard BoyesView Answer on Stackoverflow
Solution 9 - AzurenitzienView Answer on Stackoverflow
Solution 10 - AzureBruno LeitãoView Answer on Stackoverflow
Solution 11 - AzureRajatView Answer on Stackoverflow