Build a photo gallery with Azure Functions
With Azure Functions you can quickly build simple web applications. In this example you learn how to create a simple photo gallery with just three little functions. We will use Azure Blob Storage to store the files and Azure Table Storage to save metadata. You can find the example at GitHub.
Creating thumbnails
Since we do not want use the original picture as a thumbnail, we will create a function that creates proper thumbnails. For this we can use the Image Resizer template that comes with Visual Studio. The code will look like this
public static class CreateThumbnail
{
[FunctionName("CreateThumbnail")]
public static void Run(
[BlobTrigger("images/{name}", Connection = "image-blob-connection")]Stream image,
[Blob("images-sm/{name}", FileAccess.Write, Connection = "image-blob-connection")]Stream imageSmall,
[Blob("images-md/{name}", FileAccess.Write, Connection = "image-blob-connection")]Stream imageMedium)
{
var imageBuilder = ImageBuilder.Current;
var size = imageDimensionsTable[ImageSize.Small];
imageBuilder.Build(
image, imageSmall,
new ResizeSettings(size.Item1, size.Item2, FitMode.Max, null), false);
image.Position = 0;
size = imageDimensionsTable[ImageSize.Medium];
imageBuilder.Build(
image, imageMedium,
new ResizeSettings(size.Item1, size.Item2, FitMode.Max, null), false);
}
public enum ImageSize
{
Small, Medium
}
private static Dictionary<ImageSize, Tuple<int, int>> imageDimensionsTable = new Dictionary<ImageSize, Tuple<int, int>>()
{
{ ImageSize.Small, Tuple.Create(268, 200) },
{ ImageSize.Medium, Tuple.Create(536, 400) }
};
}
This function will be triggered once a new file is placed in the Azure Storage Container named images
(BlobTrigger
). It will then create two thumbnails that are saved to the containers named images-sm
and images-md
. images-sm
will contain small sized thumbnails, while images-md
will contain medium sized thumbnails.
Save metadata
Next we will save metadata of the file into Azure Table Storage. The code for this function will look like this.
public static class SavePhoto
{
[FunctionName("SavePhoto")]
public static async Task Run(
//This parameter is needed, to get the name of the blob.
[BlobTrigger("images/{name}", Connection = "image-blob-connection")]Stream myBlob,
[Table("Photos", Connection = "photo-table-connection")]IAsyncCollector<Photo> photos,
string name)
{
var photo = new Photo() { Name = name };
photo.RowKey = Guid.NewGuid().ToString();
photo.PartitionKey = photo.RowKey.Substring(0, 1);
await photos.AddAsync(photo);
}
}
We again use the BlogTrigger
to trigger the function and also use the TableAttribute
to inject a Azure Table Storage table into an IAsynCollector
. The IAsynCollector
allows us to add new entries into the Photos
table. For the moment we just store the filename. Each Azure Table Storage entry needs a RowKey
and PartitionKey
. For the RowKey
we use a new guid, while for the PartitionKey
we use the first character of the guid. There might be better partition keys, but for this example it is good enough.
Query the Table
Next we need a functions that queries and returns the photos from Azure Table Storage.
public static class GetPhotos
{
[FunctionName("GetPhotos")]
public static HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")]HttpRequestMessage req,
[Table("Photos", Connection = "photo-table-connection")]IQueryable<Photo> photos)
{
return req.CreateResponse(HttpStatusCode.OK, photos.ToList());
}
}
This time we use a HttpTrigger
to trigger the function when ever a http get request to /api/getphotos
is made. We again use the TableAttribute
to inject a Azure Table Storage table but this time we will not use IAsyncCollector
but IQueryable
. IQuerable
allows us to query the Photos
table. In this example we just return all entires of the table.
We now have an API endpoint that we can query from our client application.
Storage Container Access Policy
By default containers are set to private (which is a good default setting!). To be able to retrieve the images via http we need to change the Container Access Policy to Blob
instead of Private
. You can change this via the Azure Portal or using the Azure Storage Explorer
CORS Settings
In order to use the API endpoint from a browser, we need to configure CORS settings on our function app. In Azure Portal navigate to your function app, click on Platform features
and then on CORS
. Here you can add URLs that are allowed to use the API endpoint.
If you run it local within Visual Studio you need to add the following line to the Debug application arguments of the project host start --cors *
.
The client
I have build a simple Angular application which queries the API endpoint and displays the thumbnails. A click on the thumbnail opens the original image. You can run the client and the functions app local, using Angular CLI for the client and Visual Studio and Azure Storage Emulator for the functions app.
Conclusion
As you can see it is very easy to create a simple web application with very few lines of code using Azure Functions. There is no need to include other SDKs or APIs, since the Azure Functions SDK comes with attributes that integrated other Azure service very easy.