Uploading an Image in MVC 5 to Azure Blob Storage

An interesting customer requirement last week came up where I needed to upload an image in MVC 5 directly to Azure Blob Storage. A simplified version follows, removing some application specific logic and validation steps. To start off I first created a MVC model for image upload purposes.

ImageUploadBindingModel

1
2
3
4
5
6
public class ImageUploadBindingModel
{
[MaxFileSize(1000000, ErrorMessage = "Maximum allowed file size is 1MB")]
[DataType(DataType.Upload)]
public HttpPostedFileBase DisplayImageUpload { get; set; }
}

For additional validation control over the upload, create a custom ValidationAttribute called MaxFileSizeAttribute.

MaxFileSizeAttribute

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
public class MaxFileSizeAttribute : ValidationAttribute, IClientValidatable
{
private readonly int _maxFileSize;

public MaxFileSizeAttribute(int maxFileSize)
{

_maxFileSize = maxFileSize;
}

public override bool IsValid(object value)
{

var file = value as HttpPostedFileBase;
if (file == null)
{
return false;
}
return file.ContentLength <= _maxFileSize;
}

public override string FormatErrorMessage(string name)
{

return base.FormatErrorMessage(_maxFileSize.ToString());
}

public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{

var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(_maxFileSize.ToString()),
ValidationType = "filesize"
};

rule.ValidationParameters["maxsize"] = _maxFileSize;
yield return rule;
}
}

Now let’s turn our attention to the actual Azure logic which streams the upload direct from MVC memory to Azure Blob Storage. Note that CloudConfigurationManager is used to source relevant connection information required by the Azure libraries, something I hope to cover in another post as it’s a very relevant topic for Cloud first .Net solutions.

AzureStorage.cs

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
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;

namespace Contoso.Helpers
{
public class AzureStorage
{
public static void UploadFromStream(string uniqueBlobName, string blobContentType, Stream fileStream)
{

// Retrieve storage account from connection string
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnection"));
// Create the blob client
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve reference to a previously created container
CloudBlobContainer container = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting("StorageContainer"));
// Retrieve reference to a blob
CloudBlockBlob blockBlob = container.GetBlockBlobReference(uniqueBlobName);
// Set Blob ContentType
blockBlob.Properties.ContentType = blobContentType;
// Stream fileStream to Blob - Note: Overwrites any existing file
blockBlob.UploadFromStream(fileStream);
}
}
}

Lastly the Controller handling the upload.

UploadImage

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
public async Task UploadImage(ImageUploadBindingModel model)
{

ContosoIdentityUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

if (user == null)
{
return null;
}

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var validImageTypes = new string[]
{
"image/gif",
"image/jpeg",
"image/pjpeg",
"image/png"
};

if (model.DisplayImageUpload != null && model.DisplayImageUpload.ContentLength > 0)
{
if (!validImageTypes.Contains(model.DisplayImageUpload.ContentType))
{
ModelState.AddModelError("DisplayImageUpload", "Supported Display Image formats: GIF, JPG or PNG.");
return BadRequest(ModelState);
}
}

if (model.DisplayImageUpload != null && model.DisplayImageUpload.ContentLength > 0)
{
string uniqueBlobName = string.Format("Image_{0}.{1}", user.Id, Path.GetExtension(model.DisplayImageUpload.FileName));
string blobContentType = model.DisplayImageUpload.ContentType;

AzureStorage.UploadFromStream(uniqueBlobName, blobContentType, model.DisplayImageUpload.InputStream);
}

return Ok();
}