Geschrieben von: Christoph Wille
Kategorie: ASP.NET
This printed page brought to you by AlphaSierraPapa
Produktkataloge und Online-Bildergalerien haben (meist) eines gemeinsam: bevor man zum eigentlichen Detailbild gelangt, wird einem zuerst ein Vorschaubild, ein Thumbnail, präsentiert. Solange die Bilder offline generiert werden, kann man auch die Thumbnails offline mitgenerieren - was aber, wenn die Bilder am Server gewartet/erstellt werden, und man die Thumbnails dynamisch on the fly generieren soll?
Um die Spannung gleich abzubauen: die Thumbnailgenerierung unter .NET ist an sich ganz einfach - man muß nur die GetThumbnailImage Methode der Image Klasse aufrufen, und schon bekommt man automatisch das Thumbnail in der gewünschten Größe in die Hand gedrückt. Es ist zwar praktisch, daß es so einfach ist, hat aber den Nachteil, daß man bestimmte Arbeiten dann immer wieder per Hand macht - öffnen der Grafik, ermitteln der Skalierungverhältnisse, etc. Warum das nicht alles in eine Klasse verpacken, und immer wieder verwerten?
Das war das Stichwort - Klasse. Unsere Klasse wird Thumbnail heißen, und im Prinzip nur aus vielen polymorphen GetThumbnail Methoden bestehen (mehrere Methoden mit identem Namen aber unterschiedlicher Parameterliste). Um die Wiederverwendbarkeit noch zu steigern, kompilieren wir die Klasse in eine Assembly - damit kann ich auf die Klasse von jeder Programmiersprache aus zugreifen.
Damit zum Code (die Erklärung folgt) in Thumbnail.cs, in der die gleichnamige Klasse implementiert ist:
using System; using System.Drawing; namespace AspHeute.ThumbsUp { public class Thumbnail { public bool AbortThumbnailGeneration() { return false; } public Image GetThumbnail(string strFilename, int nScalePercentage) { Image img2Scale = Image.FromFile(strFilename); Image imgThumb = GetThumbnail(img2Scale, nScalePercentage); img2Scale.Dispose(); // cleanup return imgThumb; } public Image GetThumbnail(Image imgFullSize, int nScalePercentage) { if (nScalePercentage < 1 || nScalePercentage > 99) throw new ArgumentException("Scale percentage must be between 1 and 99"); int nImageWidth = imgFullSize.Width; int nImageHeight = imgFullSize.Height; nImageWidth = (int)((double)nImageWidth * ((double)nScalePercentage/100.0)); nImageHeight = (int)((double)nImageHeight * ((double)nScalePercentage/100.0)); return GetThumbnail(imgFullSize, nImageWidth, nImageHeight); } public Image GetThumbnail(Image imgFullSize, int nWidth, int nHeight) { Image.GetThumbnailImageAbort cb = new Image.GetThumbnailImageAbort(AbortThumbnailGeneration); return imgFullSize.GetThumbnailImage(nWidth, nHeight, cb, IntPtr.Zero); } } }
Die drei Methoden namens GetThumbnail nehmen jeweils unterschiedliche Parameter entgegen. Die erste erlaubt den Aufruf mittels Dateinamen und Skalierungsfaktor, und ruft nach Öffnen des Bildes sein Pendant mit Image in der Parameterliste auf. Diese Methode erledigt die Berechnung der Breite und Höhe des Thumbnails basierend auf dem Skalierungsfaktor, und gibt die Arbeit der Thumbnailgenerierung an die letzte GetThumbnail Implementierung weiter. Die ruft dann GetThumbnailImage auf, und liefert das Image an den jeweiligen Aufrufer zurück.
Auch wenn das auf den ersten Blick nach Overkill aussieht - ich kann ohne ein Image anzugreifen ein skaliertes Thumbnail erstellen (Methode 1), von einem geöffneten Bild einen skalierten Thumbnail erstellen (Methode 2), sowie ein völlig frei skaliertes Thumbnail erstellen (Methode 3).
Für die Kompilierung der Assembly habe ich eine Batchdatei namens build.bat mitgegeben. Nach erfolgreicher Kompilierung muß die Assembly in das bin Verzeichnis kopiert werden, und dann kann man sie in ASP.NET verwenden.
Die Verwendung der Klasse demonstriert GenThumbnailPercent.aspx - obwohl in C# erstellt, kann man die Klasse dank Kompilierung in Assembly genauso in VB.NET verwenden.
<% @Page Language="C#" %> <%@ Import Namespace="AspHeute.ThumbsUp" %> <% string strImage = Server.MapPath("Pinzgauer.jpg"); int nPercentage = 40; Thumbnail myThumb = new Thumbnail(); System.Drawing.Image imgThumb = myThumb.GetThumbnail(strImage, nPercentage); Response.Clear(); Response.ContentType = "image/jpeg"; imgThumb.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg); imgThumb.Dispose(); Response.End(); %>
Die GetThumbnail Methode wird hier mit Dateinamen und Prozentsatz der Skalierung aufgerufen. Der zurückgelieferte Thumbnail wird direkt in den OutputStream des Response Objekts geschrieben. Und um Resourcen zu schonen, disposen wir das Image sofort. Das Bild, das am Client ankommt, sieht dann so aus:
Bilder werden auf Websites nicht nur prozentuell skaliert, sondern des öfteren mit einer fixen Seitenlänge - entweder Breite oder Höhe ist fix vorgegeben, die jeweils andere Seite soll im Verhältnis skaliert werden. Um dies zu realisieren, habe ich zwei weitere polymorphe Methoden in die Klasse eingebaut:
public Image GetThumbnail(string strFilename, int nPixelSize, bool bPortrait) { Image img2Scale = Image.FromFile(strFilename); Image imgThumb = GetThumbnail(img2Scale, nPixelSize, bPortrait); img2Scale.Dispose(); // cleanup return imgThumb; } public Image GetThumbnail(Image imgFullSize, int nPixelSize, bool bPortrait) { int nImageWidth = imgFullSize.Width; int nImageHeight = imgFullSize.Height; int nScalePercentage = 0; if (bPortrait) { nScalePercentage = (int)(nPixelSize * 100.0 / (double)nImageHeight); nImageWidth = (int)((double)nImageWidth * ((double)nScalePercentage/100.0)); nImageHeight = nPixelSize; } else { nScalePercentage = (int)(nPixelSize * 100.0 / (double)nImageWidth); nImageHeight = (int)((double)nImageHeight * ((double)nScalePercentage/100.0)); nImageWidth = nPixelSize; } return GetThumbnail(imgFullSize, nImageWidth, nImageHeight); }
Wird die Breite als fix angesehen, so ist das Bild im Landscape Modus, ist die Höhe hingegen fix, nennt sich das Portrait. Deshalb: ist der Parameter bPortrait true, dann ist die Höhe fix, und umgekehrt wenn bPortrait false ist, ist die Breite fix vorgegeben. Zusammen mit der Wunschlänge der Seite rechnet man das Verhältnis aus, und bestimmt die fehlende skalierte Seite. Und schon hat man den skalierten Thumbnail.
Die Verwendung wird wieder in einem kleinen Beispiel demonstriert, GenThumbnailPixel.aspx:
<% @Page Language="C#" %> <%@ Import Namespace="AspHeute.ThumbsUp" %> <%@ Import Namespace="System.Drawing" %> <%@ Import Namespace="System.Drawing.Imaging" %> <% string strImage = Server.MapPath("Pinzgauer.jpg"); int nPixelSize = 150; bool bPortrait = false; Thumbnail myThumb = new Thumbnail(); System.Drawing.Image imgThumb = myThumb.GetThumbnail(strImage, nPixelSize, bPortrait); Response.Clear(); Response.ContentType = "image/jpeg"; imgThumb.Save(Response.OutputStream, ImageFormat.Jpeg); imgThumb.Dispose(); Response.End(); %>
Natürlich kann man die Save Methode von Image auch in eine Datei speichern lassen - das ist speziell dann interessant, wenn man Thumbnails vorgeneriert, und nicht dynamisch pro User erstellt. Solche Batchjobs sollte man aber besser auch als solche ablaufen lassen, und nicht in ASP.NET.
Das Erstellen von Thumbnails in .NET ist ein Kinderspiel - mit ein wenig Klasse kann man sogar noch einiges an Produktivität herausholen, wie dieser Artikel gezeigt hat.
This printed page brought to you by AlphaSierraPapa
Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20020225.zip
.NET Komponenten in COM+ Clients einsetzen
http:/www.aspheute.com/artikel/20020702.htm
Bildinformationen selbst ermitteln
http:/www.aspheute.com/artikel/20001130.htm
Dateien lesen in ASP.NET
http:/www.aspheute.com/artikel/20000929.htm
Einführung: C#-Klassen in ASP.NET
http:/www.aspheute.com/artikel/20001012.htm
On-the-fly Generierung von Graphiken
http:/www.aspheute.com/artikel/20000728.htm
©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.