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.
Architecture
Section titled βArchitectureβUpload β Blob Storage (raw/ container) β Blob trigger β Azure Function ββ Resize to thumbnail, medium, large ββ Convert to WebP/AVIF ββ Strip EXIF metadata ββ Write to processed/ containerKeeping raw uploads and processed output in separate containers avoids the function re-triggering itself when it writes its own output.
Blob-triggered Azure Function
Section titled βBlob-triggered Azure Functionβ[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 });}Generating multiple sizes
Section titled βGenerating multiple sizesβ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);}Key practices
Section titled βKey practicesβ- 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.
Next steps
Section titled βNext stepsβ- Serve the
processed/container through the Azure Blob Storage images guide and your Static Web Apps integration - Review cost optimization strategies once the pipeline is live β image processing changes your storage and compute cost profile