CRUD Operations with Azure Blob Storage in an Azure Function – Update & Deletion

In the preceeding parts of this series, we have delved into how to Create and Retrieve blobs using Azure Web Functions. In this part, we will look into how delete the blobs from Azure Blob Storage.

Wait a minute !! Did that we mean we skip Update ? Not really, we will have a word about it before we start with Deletion.

Update Blobs

The reason I thought Update Blobs doesn’t require its own blog post was it is quite different from what we learned for Creation (for starters, without delving too much into details). In fact, there is no difference at all.

Consider the Block Blobs – most likely, these are single files whose updation implies replacing them with a updated version. This could be an image file or an media file. This is no different from creating a new file.

You could overwrite an existing blob using the same code which learned in the previous post.

using (var stream = data.OpenReadStream())
{
    var blob = blobContainer.GetBlockBlobReference(key);
    await blob.UploadFromStreamAsync(stream);
}

With Append Blob, as the name suggests, the action is more of an “append to end of blob” scenario. This is whatthe Append blob is designed for. You could do exactly that using your learnings in the previous blog.

Deletion

Now that we have briefly spoken about the Updatation process, let us move ahead with the Deletion.

How do one delete a blob ? Well the answer is pretty simple, and is almost the same for Block and Append Blobs.

[FunctionName("DeleteFileBlockBlob")]
public static async Task<IActionResult> DeleteFileBlockBlob(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    [Blob("todos")] CloudBlobContainer blobContainer,
    ILogger log
    )
{
    string key = req.Query["key"];

    var blob = blobContainer.GetBlockBlobReference(key);
    var result = await blob.DeleteIfExistsAsync();

    return new OkObjectResult($"Item Deletion {(result ? "Success" : "Failed")}");
}

As you notice in the code above, all it takes is to get a reference to the blob using the now familiar CloudBlobContainer.GetBlockBlobReference and use the CloudBlockBlob.DeleteIfExistsAsync to delete it.

With the Append Blob, obviously, one needs to get reference to the Append Blob, instead of Block Blob, but the code feels no different. Take a look at the example below.

[FunctionName("DeleteFileAppendBlob")]
public static async Task<IActionResult> DeleteFileAppendBlob(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
[Blob("sample")] CloudBlobContainer blobContainer,
ILogger log
)
{
    string key = req.Query["key"];

    var blob = blobContainer.GetAppendBlobReference(key);
    var result = await blob.DeleteIfExistsAsync();

    return new OkObjectResult($"Item Deletion {(result ? "Success" : "Failed")}");
}

That’s hardly any different.

So we have so far, looked into basics of CRUD operations into Azure Table Storage and Azure Blob Storage. We will continue our exploration of Azure Storage options and also take time to delve deeper in the configurations/security options in the upcoming blobs.

Until then, keep coding !!! As always, all code discussed here are available in my Github. For complete series on Azure Storage, refer here

CRUD Operations with Azure Blob Storage in an Azure Function – Read

In the previous post, we examined how we could add an item to the Azure blob.

In this part, we will use an Azure function to download the file store in Azure Blob Storage.

Block Blob

Let us begin with Block Blobs.

[FunctionName("DownloadBlockBlob")]
public static async Task<IActionResult> DownloadBlockBlob(
    [HttpTrigger(AuthorizationLevel.Anonymous,"get",Route =null)] HttpRequest req,
    [Blob("todos")] CloudBlobContainer blobContainer,
    ILogger log)
{
    var key = req.Query["key"];

    var stream = new MemoryStream();
    var blob = await blobContainer.GetBlobReferenceFromServerAsync(key);
    await blob.DownloadToStreamAsync(stream);
    stream.Position = 0;
    return new FileStreamResult(stream, "application/octet-stream") { FileDownloadName = key };
}

This turns out to be pretty straightforward, isn’t it. The CloudBlobContainer.GetBlobReferenceFromServerAsync gets the reference to the blob we are interested in using the blob name, in this case, passed on as a query parameter. We could then use the CloudBlob.DownloadToStreamAsync method to download the contents of the blob to a stream, which could be later pass back using the FileStreamResult.

One obvious question would be why use a azure function in the above case, when there is no authentication. One could directly use a Http Get Request. Partially, that is true, however, do not forget this is only an example and most often than note, authentication would not be anonymous and there could be other things that you would like your azure function to during the download, like a log or updating/fetching another document.

Of course, you could simplify things using input bindings. For example, the above code could be rewritten using binding as the following.

[FunctionName("DownloadBlockBlobUsingBinding")]
public static async Task<IActionResult> DownloadBlockBlobUsingBinding(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "DownloadBlockBlobUsingBinding/{fileName}")] HttpRequest req,
[Blob("todos/{fileName}",FileAccess.Read)] Stream blob,
string fileName,
ILogger log)
{
    var memoryStream = new MemoryStream();
    await blob.CopyToAsync(memoryStream);
    memoryStream.Position = 0;
    return new FileStreamResult(memoryStream, "application/octet-stream") { FileDownloadName = fileName };
}

Append Blob

To download a file from an Append Blob is no different from what we have seen with Block Blob.

[FunctionName("DownloadAppendBlob")]
public static async Task<IActionResult> DownloadAppendBlob(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
[Blob("sample")] CloudBlobContainer blobContainer,
ILogger log)
{

    var key = req.Query["key"];

    var stream = new MemoryStream();
    var blob = blobContainer.GetAppendBlobReference(key);
    await blob.DownloadToStreamAsync(stream);
    stream.Position = 0;
    return new FileStreamResult(stream, "text/plain") { FileDownloadName = key };
}


The only point of importance while learning would be to remember that Append Blobs are not supported by the Storage emulator. For rest of the code look quite similar to the Block blob, sole difference being we use a different method to get reference to the blob – CloudContainer.GetAppendBlobReference.

As mentioned in the earlier post, we will address the Page blob separately. But for now, go ahead and play around with the Block Blob and Append Blob. You could access the source code explained in this code in my Github

CRUD Operations with Azure Blob Storage in an Azure Function – Create, Part 1

With Azure Blob storage, Microsoft provides an easy to use cloud based storage solution for storing massive amount of data, particularly unstructured data. These are ideal for storing media files which could be served directly to browser, maintaining log files among others.

There are three components of Blob storage one needs to be aware of.

  • Storage Account – The unique namespace for your azure data.
  • Container – Containers are similar to Directory in a file system, and is used to organize your blobs into sub sections.
  • Blobs – Reflects the data to be stored.

The blobs themselves are of 3 types.

  • Block Blobs : Ideal for storing image and other media files for streaming.
  • Append Blobs : Ideal for logging (append to end)
  • Page Blobs : Ideal for frequent read/write operations

That was a short introduction on the Blob Storages, let us now look at some code to create a new item in the blob storage.

We will stick to Azure Functions, and keep the code as simple as possible. As mentioned in the series opener, the more advanced topics including security would be discussed in a seperate posts.

Create Item in Block Blob

We will begin with the block blob first. In the very first example, we will add a text file to the blob, contents of which are passed via the request. I guess that would be the simplest way to begin the journey.

[FunctionName("AddItemBlockBlob")]
public static async Task<IActionResult> AddItemBlockBlob(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    [Blob("todos")] CloudBlobContainer blobContainer,
    ILogger log)
{
    log.LogInformation("Request Recieved");

    await blobContainer.CreateIfNotExistsAsync();

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    var data = JsonConvert.DeserializeObject<TodoDto>(requestBody);

    data.Id = Guid.NewGuid().ToString();

    var serializedData = JsonConvert.SerializeObject(data);

    var blob = blobContainer.GetBlockBlobReference($"{data.Id}.json");
    await blob.UploadTextAsync(serializedData);
    return new OkObjectResult($"Item added to blob with Id = {data.Id}");
}

public class TodoDto
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public bool IsCompletd { get; set; }
}


CloudBlobContainer references the container in which our blob resides. If the container does not exists, we could create it using the CloudBlobContainer.CreateIfNotExistsAsync method.

Since we want to store our data in a Block Blob, we use CloudBlockBlob.GetBlockBlobReference method to get reference to the blob and use the CloudBlockBlob.UploadTextAsync method to upload the text data to the blob, referred by the blob name. In our first example, it is a Guid(followed by a string .json – but this is only for bringing more clarity to content of blob, you could choose a different name as well).

That was pretty clean, thanks to the rich set of APIs defined by Microsoft. There is however a behavior one needs to be aware of. If we were to upload text again to the same blob (refered by same blob name), the content would be replaced.

Upload a file to Block Blob

As the second example, let us attempt to upload an image file to the blob.

[FunctionName("UploadFileBlockBlob")]
public static async Task<IActionResult> UploadFileBlockBlob(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    [Blob("todos")] CloudBlobContainer blobContainer,
    ILogger log
    )
{
    string key = req.Query["key"];
    var data = req.Form.Files["file"];
    using (var stream = data.OpenReadStream())
    {
        var blob = blobContainer.GetBlockBlobReference(key);
        await blob.UploadFromStreamAsync(stream);
    }
    return new OkObjectResult($"Item added to blob with Id = {key}");
}

In this example, we are retrieving the contents of the file from the HttpRequest and using the CloudBlockBlob.UploadFromStreamAsync method to upload the file to the blob.

Of course, Bindings are applicable here as well, and you could reduce fair bit of code with help of output binding.

[FunctionName("UploadFileBlockBlobBinding")]
public static async Task<IActionResult> UploadFileBlockBlobBinding(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "UploadFileBlockBlobBinding/{filename}")] HttpRequest req,
    [Blob("todos/{filename}",FileAccess.Write)] Stream fileStream,
    string filename,
    ILogger log)
{
    log.LogInformation("Request Recieved");
    var data = req.Form.Files["file"];
    var inputDataStream = data.OpenReadStream();
    await inputDataStream.CopyToAsync(fileStream);
    return new OkObjectResult($"Item added to blob with Id = {filename}");
}

The parameter fileStream would refer to the blob specified by the todos/{filename} where the filename is passed via the Http request.

The inputDatastream, read from the HttpRequest is being copied over to the fileStream using the CopyToAsync method, ensuring the blob has been updated with the stream of data from the request.

Create a Append Blob

Let us now attempt to create another type of blob – Append Blob. Unfortunately, the Azure Storage Emulator does not support the Append Blob, so do make sure you have your connections string in place before trying the following code.

[FunctionName("AddToAppendBlob")]
public static async Task<IActionResult> AddToAppendBlob(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    [Blob("sample")] CloudBlobContainer blobContainer,
    ILogger log)
{
    log.LogInformation("Request Recieved");

    await blobContainer.CreateIfNotExistsAsync();
    var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    var data = JsonConvert.DeserializeObject<LogMessage>(requestBody);

    var blob = blobContainer.GetAppendBlobReference("applog.txt");
    if (!blob.Exists())
    {
        blob.CreateOrReplace();
    }
    await blob.AppendTextAsync($"User:{data.User}, Message:{data.Message}");

    return new OkObjectResult($"Item added to blob");
}

We retrieve a reference to the Append Blob using the CloudBlobContainer.GetAppendBlobReference method. We then use the CloudAppendBlob.AppendTextAsync to append the data to the end of the existing blob.

The Page Blobs has a bit more story to tell, which is why we will reserve it for another post. But as you have already seen, Microsoft’s rich API makes it easy to create blobs, as it was the case with Azure Table Storage.