Azure Blob Storage
Azure Blob Storage is used to store binary assets — camera images (single, panoramic, pre-stitch), videos, location boundary files, device configuration data, and map images. The Infrastructure package provides a generic base class and 7 specialized blob repositories, each scoped to a specific container.
Registration
Blob storage repositories are registered as part of the AddAlertCAStorageServices extension method:
services.AddAlertCAStorageServices(configuration);
This registers BlobServiceClient via Azure.Extensions and all blob repositories as scoped services.
Configuration
{
"BlobStorageOptions": {
"AccountName": "",
"ContainerName": "",
"VideoContainerName": "",
"LocationsContainerName": "",
"DeviceDataContainerName": "",
"MapImagesContainerName": ""
}
}
The BlobServiceClient URL is constructed from the AccountName: https://{AccountName}.blob.core.windows.net. Authentication uses DefaultAzureCredential.
Architecture
Note that SingleImageStorageRepository, PanoImageStorageRepository, and PreStitchImageStorageRepository all share the same ImageStorageRepository implementation pointing to the same container — they are differentiated by their marker interfaces for DI registration.
Base Class: AzureStorageRepository<T>
All blob repositories inherit from AzureStorageRepository<T>, which provides:
| Method | Description |
|---|---|
DoesBlobExists(string fileName) | Check if a blob exists |
GetFileBlobAsync(string fileName) | Download blob with metadata as BlobEntity |
UploadFileBlobAsync(byte[] content, string fileName, Dictionary<string, string> metadata) | Upload with metadata |
UploadFileBlobAsync(byte[] content, string fileName, bool overwrite) | Upload without metadata |
UploadZipBlobAsync(List<BlobZipEntity> entities, string fileName, CompressionLevel level) | Create and upload ZIP archive |
DeleteFromBlob(string fileName) | Delete a blob |
Repositories
ImageStorageRepository
Implements ISingleImageStorageRepository, IPanoImageStorageRepository, and IPreStitchImageStorageRepository — a single implementation for all image types.
| Method | Description |
|---|---|
UploadImageAsync(byte[] imageArray, string blobName, Dictionary<string, string> metadata) | Upload image with metadata (PTZ position, timestamps, etc.) |
UploadImageAsync(byte[] imageArray, string blobName) | Upload image without metadata |
UploadImageAsZipAsync(List<BlobZipEntity> entities, string blobName, CompressionLevel level) | ZIP multiple images (used for pre-stitch archives) |
DownloadImagesAsync(List<string> blobNames) | Parallel bulk download |
VideoStorageRepository
Interface: IVideoStorageRepository
| Method | Description |
|---|---|
UploadVideoAsync(byte[] videoArray, string blobName) | Upload video |
DownloadVideoAsync(List<string> blobNames) | Parallel bulk download |
LocationFileStorageRepository
Interface: ILocationFileStorageRepository
| Method | Description |
|---|---|
UploadLocationFileAsync(byte[] bytes, string blobName) | Upload with overwrite |
DeleteLocationFileAsync(string blobName) | Delete file |
DownloadLocationFilesAsync(List<string> blobNames) | Parallel bulk download |
DeviceDataRepository
Interface: IDeviceDataRepository
| Method | Description |
|---|---|
UploadDeviceDataFileAsync(byte[] fileData, string blobName) | Upload device data |
DeleteDeviceDataFileAsync(string blobName) | Delete file |
DownloadDeviceDataFilesAsync(List<string> blobNames) | Parallel bulk download |
MapImagesRepository
Interface: IMapImagesRepository
| Method | Description |
|---|---|
UploadMapImagesFileAsync(byte[] fileData, string blobName) | Upload map/basemap image |
Usage Example
using Alert.CA.Infrastructure.Repositories.AzureStorageRepositories.Interfaces;
public class ImageService(
ISingleImageStorageRepository imageRepo,
IPanoImageStorageRepository panoRepo)
{
public async Task StoreImage(byte[] image, string blobName, Dictionary<string, string> metadata)
{
await imageRepo.UploadImageAsync(image, blobName, metadata);
}
public async Task<List<BlobEntity>> DownloadBatch(List<string> blobNames)
{
return await panoRepo.DownloadImagesAsync(blobNames);
}
}