How to delete a folder within an Azure blob container
AzureAzure StorageAzure Blob-StorageAzure Problem Overview
I have a blob container in Azure called pictures
that has various folders within it (see snapshot below):
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/
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
Azure CLI
Try using theFor 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
You can now use the delete activity in ADF to delete any file /blob. It will delete all the files inside