I wanted to share some code I wrote recently for uploading a file to a .NET Core Web API and writing it to Blob Storage, as well as, downloading the file from the .NET Core Web API.
As a bonus, we will use SAS tokens to read and write to Blob Storage.
Let’s get started.
Create an Azure Storage account or use an existing one.
Open Visual Studio or VS Code and Create a new .NET Core Web API project.
Add a folder called Helpers
.
Add two files, one called AzureStorageBlobOptions.cs
and another called AzureStorageBlobOptionsTokenGenerator.cs
.
Configuration will be provided by AzureStorageBlobOptions
and the SAS Token will be generated by AzureStorageBlobOptionsTokenGenerator
.
1 2 3 4 5 6 7 8 9 10 11 12 |
public class AzureStorageBlobOptions { public string AccountName { get; set; } public string AccountKey { get; set; } public string ConnectionString { get; set; } public string FilePath { get; set; } public IOptions<AzureStorageBlobOptions> AsOptions() { return Options.Create(this); } } |
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 |
public class AzureStorageBlobOptionsTokenGenerator { private readonly IOptions<AzureStorageBlobOptions> _options; public AzureStorageBlobOptionsTokenGenerator( IOptions<AzureStorageBlobOptions> options) { _options = options; } public string GenerateSasToken( string containerName) { return this.GenerateSasToken( containerName, DateTime.UtcNow.AddSeconds(30)); } public string GenerateSasToken( string containerName, DateTime expiresOn) { var cloudStorageAccount = CloudStorageAccount.Parse(_options.Value.ConnectionString); var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient(); var cloudBlobContainer = cloudBlobClient.GetContainerReference(containerName); var permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write; string sasContainerToken; var shareAccessBlobPolicy = new SharedAccessBlobPolicy() { SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5), SharedAccessExpiryTime = expiresOn, Permissions = permissions }; sasContainerToken = cloudBlobContainer.GetSharedAccessSignature(shareAccessBlobPolicy, null); return sasContainerToken; } } |
In the appsettings.Development.json
, Add a section called AzureStorageBlobOptions
and Add configuration keys and Update the key values based on your Azure Storage account and Blob container.
1 2 3 4 5 |
"AzureStorageBlobOptions": { "AccountName": "AZURE_STORAGE_ACCOUNT_NAME", "ConnectionString": "AZURE_STORAGE_ACCOUNT_CONNECTION_STRING", "FilePath": "AZURE_STORAGE_ACCOUNT_BLOB_CONTAINER_NAME" } |
In the Startup.cs
file add dependency injection support for the AzureStorageBlobOptions
and AzureStorageBlobOptionsTokenGenerator
classes.
1 2 3 4 |
services.Configure<AzureStorageBlobOptions>( options => Configuration.GetSection(nameof(AzureStorageBlobOptions)).Bind(options)); services.AddSingleton<AzureStorageBlobOptionsTokenGenerator, AzureStorageBlobOptionsTokenGenerator>(); |
In the Controllers
folder, Add a controller called FilesController
.
Update the FilesController
constructor to accept two parameters, one for AzureStorageBlobOptions
and AzureStorageBlobOptionsTokenGenerator
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[Route("api/files")] [ApiController] [Produces("application/json")] public class FilesController : ControllerBase { private readonly IOptions<azurestoragebloboptions> _azureStorageBlobOptions; private readonly AzureStorageBlobOptionsTokenGenerator _azureStorageBlobOptionsTokenGenerator; public FilesController( IOptions<azurestoragebloboptions> azureStorageBlobOptions, AzureStorageBlobOptionsTokenGenerator azureStorageBlobOptionsTokenGenerator) { _azureStorageBlobOptions = azureStorageBlobOptions; _azureStorageBlobOptionsTokenGenerator = azureStorageBlobOptionsTokenGenerator; } }</azurestoragebloboptions></azurestoragebloboptions> |
Add a PostAsync
method to support file upload, you will also want to add a Consumes
attribute to handle the file upload.
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 |
[HttpPost] [Consumes("application/json", "application/json-patch+json", "multipart/form-data")] public async Task<iactionresult> PostAsync( IFormFile file) { var sasToken = _azureStorageBlobOptionsTokenGenerator.GenerateSasToken( _azureStorageBlobOptions.Value.FilePath); var storageCredentials = new StorageCredentials( sasToken); var cloudStorageAccount = new CloudStorageAccount(storageCredentials, _azureStorageBlobOptions.Value.AccountName, null, true); var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient(); var cloudBlobContainer = cloudBlobClient.GetContainerReference( _azureStorageBlobOptions.Value.FilePath); var blobName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}"; var cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(blobName); cloudBlockBlob.Properties.ContentType = file.ContentType; using (var fileStream = file.OpenReadStream()) { await cloudBlockBlob.UploadFromStreamAsync(fileStream); } return Ok(new { name = blobName }); } </iactionresult> |
Add a GetAsync
method to support file download.
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 |
[HttpGet("{id}")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0017:Simplify object initialization", Justification = "<Pending>")] public async Task<IActionResult> GetAsync( string id) { var sasToken = _azureStorageBlobOptionsTokenGenerator.GenerateSasToken( _azureStorageBlobOptions.Value.FilePath); var storageCredentials = new StorageCredentials( sasToken); var cloudStorageAccount = new CloudStorageAccount(storageCredentials, _azureStorageBlobOptions.Value.AccountName, null, true); var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient(); var cloudBlobContainer = cloudBlobClient.GetContainerReference( _azureStorageBlobOptions.Value.FilePath); var blobName = id; var cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(blobName); var ms = new MemoryStream(); await cloudBlockBlob.DownloadToStreamAsync(ms); return File(ms.ToArray(), cloudBlockBlob.Properties.ContentType); } |
Run the Web API.
Open Postman, and Create a new POST
request.
Enter the API URL, in my case it was https://localhost:44327/api/files
.
Set Body to form-data
.
Add a key called file
, make sure to change the type to File, defaults to Text.
Add the file to upload and Send the request.
If the call is successful, you should receive an OK
response with the new name of the file uploaded.
Now to download the file.
Create a new GET
request in Postman.
Enter the API URL, in my case it was https://localhost:44327/api/files/RETURN_FILE_NAME
, and Send the request.
If the call is successful, you should see the image displayed in Postman.
Thanks for reading! And please feel free to share any feedback, it is much appreciated!
Discover more from Matt Ruma
Subscribe to get the latest posts sent to your email.
Hello Sir,
Thanks for the article.
But then i tried this code, IOptions in AzureStorageBlobOptions is throwing an error.
“Using the generic type IOptions requires 1 type arguments.
Any help is greatly appreciated.
define the prperty like this IOptions
Thank you so much.. It works for me 🙂