Image Handler in ASP.NET Core

Der ASP.NET Entwickler egal ob Webforms oder MVC wird Module und Handler kennen. Die IIS Request Pipeline lässt sich so erweitern oder verändern. Der Webforms Entwickler muss nicht einmal einen Handler registrieren, wenn er den Standard  Handler .ashx verwendet. So lässt sich beliebiger Code einer Klasse ausführen statt einer ASPX Seite. Etwas was von mir gern und oft genutzt wird um Thumbnails von Bildern darzustellen. Meistens mehrfach für Listen, aber im Kern wie folgt

   1:     <img src="thumbnail.ashx?img=hannesbrille.jpg" />

Ein mehrere MB großes Bild wird verkleinert und komprimiert auf wenige KB per Logik live beim Request.

Zunächst wird eine Klasse mit beliebigen Namen erzeugt. Der Konstruktor muss einen speziellen Parameter aufweisen. Da es sich um eine Request Pipeline handelt, könnte man den Request für nachgelagerte Module unterbinden. Die Methode Invoke ist er eigentliche Einstiegspunkt jedes Moduls ala Main.

Die zweite statische Klasse wird in diesem Fall benötigt um eine spezielle Http Route anlegen zu können, wie im nächsten C# Code Listing erläutert wird. Die Erweiterung Extension zum Klassennamen scheint Konvention zu sein. Die genaue Quelle in der Dokumentation vermisse ich noch. Die eigentliche statische Methode UseImageThumbhandler muss nicht so benannt werden, wird aber in der Startup.cs benötigt.

   1:  public class ImageThumbHandler
   2:  {
   3:   //Pflicht Middleware Pipeline
   4:   public ImageThumbHandler(RequestDelegate next)
   5:   { }
   6:   public async Task Invoke(HttpContext context)
   7:   {
   8:  .....
   9:   
  10:  public static class ImageThumbHandlerExtensions
  11:  {
  12:    public static IApplicationBuilder UseImageThumbHandler(this IApplicationBuilder builder)
  13:    {
  14:       return builder.UseMiddleware<ImageThumbHandler>();
  15:    }
  16:  }
  17:   
  18:   

Den ganzen Aufwand muss man betreiben um diesen Service in der Startup.cs zu konfigurieren. In der Methode Configure, am besten ganz am Schluss wird folgender Code eingefügt.

   1:  app.MapWhen(
   2:        context => context.Request.Path.ToString().Contains("thumbnail.ashx"),
   3:        appBranch =>
   4:        {
   5:             appBranch.UseImageThumbHandler();
   6:         });

Dieses C# Schnippsel konfiguriert eine neue Middleware die sich in die Request Pipeline des Webservers (IIS) eingeklinkt. Aber nur wenn in der Url thumbnail.ashx vorkommt. Im Regelfall wird Middleware ala App.UseMvc ohne weitere Parameter registriert. In diesem Fall soll das UseImageThumbhandler (aus dem Code vorher) nur bei passenden Pfad oder auch Erweiterungen konfiguriert sein. Im Builder der statischen Methode wird dann per UseMiddleWare die Klasse mit der Logik (ImageThumbHandler) injiziert. So habe ich das jedenfalls verstanden.

Ich habe alten Code für Image Vorschaubildergenerierung kopiert und mehrere böse Überraschungen erlebt. ASP.NET Core ist an dieser Stelle wenig kompatibel. Zuerst muss man ein Nuget Paket nachinstallieren um überhaupt die Funktionen aus System.Drawing in ASP.NET core verwenden zu können. Statt Request.Querystring nimmt man .Query. Das Server Objekt und Mappath sucht man vergeblich. Die einfachste Variante GetCurrentDirectory.

Das gerechnete Image wird in einem speziellen Verzeichnis thumbs abgelegt um nicht bei jedem Request es neu berechnen zu müssen. Hier könnte man auch noch client seitiges Caching implementieren. Einen Output Stream habe ich auf dem Response Objekt nicht gefunden. So das nach dem schreiben die Datei noch einmal gelesen werden muss, um sie mit SendFileAsync dem Browser zu überreichen.

   1:   context.Response.ContentType = "image/jpg";
   2:   string img = context.Request.Query["img"];
   3:   var pfad = Path.Combine(
   4:                          Directory.GetCurrentDirectory(),
   5:                           @"wwwroot\images\" ,img);
   6:   var thumbpfad = Path.Combine(
   7:                          Directory.GetCurrentDirectory(),
   8:                           @"wwwroot\images\thumbs\" ,img);
   9:   if (File.Exists(pfad))
  10:     {
  11:      if (!File.Exists(thumbpfad))
  12:      {
  13:       using (FileStream fs = new FileStream(pfad, FileMode.Open, FileAccess.Read))
  14:         using (var image = new Bitmap(fs)) //Install-Package Microsoft.Windows.Compatibility -Version 2.0.1
  15:           {
  16:            var thmb = new Bitmap(200, 150);
  17:            using (var graphics = Graphics.FromImage(thmb))
  18:            {
  19:               graphics.CompositingQuality = CompositingQuality.HighSpeed;
  20:               graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  21:               graphics.CompositingMode = CompositingMode.SourceCopy;
  22:               graphics.DrawImage(image, 0, 0, 200, 150);
  23:               thmb.Save(thumbpfad);
  24:             }
  25:            }
  26:          }
  27:          await context.Response.SendFileAsync(thumbpfad);
  28:       

Diese und weitere Themen findet ihr auf der ADC konferenz.

Kommentare sind geschlossen