Skip to content

Automated Image Processing Workflows

Manually resizing and optimizing every image before upload doesn’t scale. An event-driven pipeline β€” a Blob-triggered Azure Function that fires the moment a new image lands in storage β€” handles resizing, format conversion, and optimization automatically, without touching your upload code.

Upload β†’ Blob Storage (raw/ container) β†’ Blob trigger β†’ Azure Function
β”œβ”€ Resize to thumbnail, medium, large
β”œβ”€ Convert to WebP/AVIF
β”œβ”€ Strip EXIF metadata
└─ Write to processed/ container

Keeping raw uploads and processed output in separate containers avoids the function re-triggering itself when it writes its own output.

[Function("ProcessImage")]
public async Task Run(
[BlobTrigger("raw/{name}", Connection = "AzureWebJobsStorage")] Stream imageStream,
string name,
[Blob("processed/{name}", FileAccess.Write, Connection = "AzureWebJobsStorage")] Stream output)
{
using var image = await Image.LoadAsync(imageStream);
// Resize while preserving aspect ratio
image.Mutate(x => x.Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(1200, 1200)
}));
await image.SaveAsWebpAsync(output, new WebpEncoder { Quality = 80 });
}

A single upload commonly needs several derived sizes β€” thumbnail, card, full β€” for responsive <img srcset> markup:

var sizes = new Dictionary<string, int> {
["thumbnail"] = 200,
["card"] = 600,
["full"] = 1600
};
foreach (var (label, maxDimension) in sizes)
{
using var resized = image.Clone(x => x.Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(maxDimension, maxDimension)
}));
var blobClient = processedContainer.GetBlobClient($"{label}/{name}");
using var ms = new MemoryStream();
await resized.SaveAsWebpAsync(ms, new WebpEncoder { Quality = 80 });
ms.Position = 0;
await blobClient.UploadAsync(ms, overwrite: true);
}
  • Separate raw and processed containers β€” prevents infinite trigger loops and lets you apply different lifecycle/retention policies to each (raw uploads can expire sooner than processed output that’s actively served).
  • Strip EXIF metadata on the processed copies β€” GPS coordinates and device info in uploaded photos shouldn’t end up served to the public.
  • Convert to WebP/AVIF β€” meaningfully smaller than JPEG/PNG at equivalent visual quality, directly reducing both storage and CDN egress cost.
  • Idempotency: if the function can retry (Azure Functions’ at-least-once trigger delivery), overwriting the same output path is safe; avoid designs where a retry produces a different result.