How to create CRUD application using Azure Blob storage service?
Microsoft’s Azure Blob storage service is really a good option for your application if you are storing images, videos and any file. In this post, I’ll tell you how to create CRUD application using Azure Blob storage service. By CRUD application I mean create, read, update and delete a blob either for a user or system use.
If you are following my blog, I recently published a post to explain how to upload any file to Azure Blob storage service and how to use Shared Access Signature to share private blob in Azure. I’d recommend you to read this post to get the basic of blob storage service. The front-end application is using ASP.NET MVC framework.
Both the posts above use same project and I’m extending that project to make it a CRUD application. The source code can be found using this repository. Feel free to use the code or fork this repository. This application basically illustrates how to allow a user to upload image(s), list all the images by reading all of the images from a container and allow user to delete the images.
Access Azure Blob Service from front-end application
As such I’m using ASP.NET MVC for front-end, like most of the Azure services, to consume this blob service we need a client package from Nuget Package Manager in the project named as WindowsAzure.Storage
and the package version that is used for this post is 8.2.0
. The client allows to connect to all types of storages in Azure like blob, table, file and queues. For blob storage, we will be using CloudBlobClient
class.
Image Storage Service
The extended Image Storage Service code is shown below. This class named ImageStorageService
encapsulates usage of CloudBlobClient
class instance. The controller of my application doesn’t need to use the client directly. Please read the basics of using CloudBlobClient
class here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
public class ImageStorageService { private readonly CloudBlobClient _cloudBlobClient; private readonly CloudBlobContainer _container; private readonly CloudBlockBlob _blob; private readonly Uri _blobServiceEndpoint = new Uri(ConfigurationManager.AppSettings.Get("blobServiceEndpoint")); public ImageStorageService() { var credentials = new StorageCredentials("storage account name", "enter your storage account access key here or get from web.config"); _cloudBlobClient = new CloudBlobClient(_blobServiceEndpoint, credentials); _container = _cloudBlobClient.GetContainerReference("private-images"); } public Uri GetResourceUri(string resourceId) { return new Uri(_blobServiceEndpoint, $"/images/{resourceId}"); } public Uri GetResourceUriWithSas(string resourceId) { var sasPolicy = new SharedAccessBlobPolicy() { Permissions = SharedAccessBlobPermissions.Read, SharedAccessStartTime = DateTime.Now.AddMinutes(-10), SharedAccessExpiryTime = DateTime.Now.AddMinutes(30) }; CloudBlockBlob blob = _container.GetBlockBlobReference(resourceId); string sasToken = blob.GetSharedAccessSignature(sasPolicy); return new Uri(_blobServiceEndpoint, $"/private-images/{resourceId}{sasToken}"); } public async Task<string> SaveImage(Stream imageInputStream) { string id = Guid.NewGuid().ToString(); CloudBlockBlob blob = _container.GetBlockBlobReference(id); await blob.UploadFromStreamAsync(imageInputStream); return id; } public List<CloudBlockBlob> GetImages() { var blockBlobs = new List<CloudBlockBlob>(); IEnumerable<IListBlobItem> blobs = _container.ListBlobs(); IList<IListBlobItem> blobItems = blobs as IList<IListBlobItem> ?? blobs.ToList(); if (blobItems.Count == 0) return blockBlobs; foreach (IListBlobItem blobItem in blobItems) { if (blobItem is CloudBlockBlob) { blockBlobs.Add(blobItem as CloudBlockBlob); } } return blockBlobs; } public async Task<bool> DeleteImage(string resourceId) { CloudBlockBlob blob = _container.GetBlockBlobReference(resourceId); return await blob.DeleteIfExistsAsync(); } } |
As you can see in the constructor, the client uses an existing container to add or read or delete blobs. In the real world, a user may have access to one or more containers where a container can be created dynamically on-demand by using the CreateBlobContainer
class.
Also, the primary key should be stored at a safe place. Definitely not in code or web.config file. Utilise Azure’s Key Vault for this purpose in your applications.
Create/Update
Below is the SaveImage
method from the code above that creates/saves the blob to the container. It take the stream as input of the file like image or video and saves it to the container by uploading it.
A unique name is used so that we can get a reference to a blob and as such no blob exists by that name, the container creates a blob for the next step i.e. upload from stream. For updating a blob, an existing blob reference can be used in the same way and the modified or new stream can be uploaded in the same manner as shown below:
1 2 3 4 5 6 7 8 9 |
public async Task<string> SaveImage(Stream imageInputStream) { string id = Guid.NewGuid().ToString(); CloudBlockBlob blob = _container.GetBlockBlobReference(id); await blob.UploadFromStreamAsync(imageInputStream); return id; } |
Read
The method named GetImages
is used to read all the blobs from the container. We are only interested in blobs of type CloudBlockBlob
as that is the type of blob this application actually saved so it makes sense to read just that type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public List<CloudBlockBlob> GetImages() { var blockBlobs = new List<CloudBlockBlob>(); IEnumerable<IListBlobItem> blobs = _container.ListBlobs(); IList<IListBlobItem> blobItems = blobs as IList<IListBlobItem> ?? blobs.ToList(); if (blobItems.Count == 0) return blockBlobs; foreach (IListBlobItem blobItem in blobItems) { if (blobItem is CloudBlockBlob) { blockBlobs.Add(blobItem as CloudBlockBlob); } } return blockBlobs; } |
Delete
With the provided blob reference, a blob can be deleted easily as shown below:
1 2 3 4 5 6 |
public async Task<bool> DeleteImage(string resourceId) { CloudBlockBlob blob = _container.GetBlockBlobReference(resourceId); return await blob.DeleteIfExistsAsync(); } |
Blob Uri with Shared Access Signature (SAS)
A SAS token is used to share private blob with anonymous user. Read about this more in detail in my previous post.
Controller
Refer to the project to see the views for this application. The controller code is mentioned below that has some action to take care of get and post actions. Post action is used to upload and delete an image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
public class ImagesController : Controller { private ImageStorageService _imageStoreService; public ImagesController() { _imageStoreService = new ImageStorageService(); } public ActionResult Index() { List<CloudBlockBlob> blobs = _imageStoreService.GetImages(); return View(blobs); } [HttpPost, ValidateAntiForgeryToken] public async Task<ActionResult> Upload(HttpPostedFileBase image) { if (image != null) { string imageId = await _imageStoreService.SaveImage(image.InputStream); return RedirectToAction("ShowImage", new { id = imageId }); } return View("Index"); } public ActionResult ShowImage(string id) { // use this for container that has access type set to Blob // Uri imageUri = _imageStoreService.GetResourceUri(id); var viewModel = new ImageViewerViewModel() { Uri = _imageStoreService.GetResourceUriWithSas(id), ResourceId = id }; return View(viewModel); } [HttpPost, ValidateAntiForgeryToken] public async Task<ActionResult> DeleteImage(string id) { if (id == null) throw new ArgumentNullException(nameof(id)); await _imageStoreService.DeleteImage(id); return RedirectToAction("Index"); } } |
This sums up my post to explain how to create CRUD application using Azure Blob storage service. Refer to the repository to play with this project. Please subscribe to my website to get updates for similar posts.