# Flinke Fotos > von [Ulrich Hilger](https://uhilger.de), 19. Januar 2020 Die Verwaltung und Darstellung von Bildern über eine Webanwendung verlangt mehr Aufwand als im Vergleich dazu der Umgang mit einfachen Webseiten. Alle Inhalte von Webseiten müssen zunächst über das Netzwerk zum Gerät übertragen werden, auf dem sie betrachtet werden. Die Menge an Daten, die geladen werden muss, bestimmt wie lange es dauert, bis die Seite angezeigt werden kann. Webseiten enthalten vergleichsweise wenig (HTML- und CSS-) Text während Bilddateien deutlich mehr Datenvolumen einnehmen. Eine passende Logik muss ineinander greifen um Bilder ohne Geschwindikeitseinbußen über das Internet verwenden zu können. Dieser Beitrag wirft dazu einen Blick auf Methoden zum Umgang mit Bildern im Web. ## Konzept zum Umgang mit Bildern Bevor ein bestimmter Umgang mit Bildern umgesetzt werden kann will dieser zunächst konzeptionell umrissen sein. Hier folgend daher zunächst eine allgemeine Betrachtung. ### Originalbilder Eine Fotografie liegt entweder zunächst im RAW-Format der jeweiligen Digitalkamera oder auf Film als Negativ oder Diapositiv vor. Jene Originalbilder werden zunächst in eine digitale Form überführt, die von einem Browser dargestellt werden kann, z.B. in das [JPEG](https://de.wikipedia.org/wiki/JPEG) oder [PNG](https://de.wikipedia.org/wiki/Portable_Network_Graphics)-Format. Das geschieht üblicherweise bei der Bearbeitung von Digitalbildern oder beim Scannen der Filme. ### Bildgrößen und Datenmenge Fotografien im [Kleinbildformat](https://de.wikipedia.org/wiki/35-mm-Film) können abhängig von Kamera bzw. Scanner dabei 6.000 Bildpunkte und mehr entlang der längsten Kante und Dateigrößen von unkomprimiert ca. 25 Megabyte haben. Zur Darstellung auf einer Webseite genügen hingegen Größen von beispielsweise 1.920 Bildpunkten oder, je nach Verwendung des Bildes auf der Seite auch weniger. Neben einer geringeren Größe kann Kompression die Datenmenge zusätzlich verringern. Eine Kompression von ca. 70% der Originalqualität erreicht für ein Bild mit 1.920 Bildpunkten je nach Bildinhalt eine Dateigröße von ca. 250 - 500 Kilobyte. ### Miniaturansichten Neben einer möglichst großen Fassung, die für die Betrachtung einzelner Bilder vorgesehen ist, wird in aller Regel auch die Miniaturansicht eines Bildes benötigt, um mehrere Bilder in einer Übersicht darstellen zu können. Je mehr Bilder in eine Übersicht passen sollen, desto kleiner fallen die sich dafür ergebenden Einzelbilder aus. Die Miniaturansicht eines Bildes mit 120 Bildpunkten nimmt komprimiert gerade noch 2-4 Kilobyte Dateigröße ein. ### Lösungsansatz Um für die Darstellung eines Bildes nicht mehr Daten übertragen zu müssen als nötig, werden für jedes Bild, das auf einer Webseite erscheinen soll, mehrere Fassungen erzeugt und fertig zur Darstellung auf einem Server hinterlegt. Eine Webanwendung, die Bilder vom Server darstellt, entscheidet zur Laufzeit, welche Fassung eines Bildes gerade erforderlich ist, sei es Originalgröße, Miniaturansicht oder eine Zwischengröße und ruft stets die zur gerade erforderlichen Größe passende Fassung vom Server ab. Dazu muss freilich die Webanwendung die auf dem Server verfügbaren Größen kennen. Ein Server soll für jedes Originalbild von beipsielsweise 1.920 Bildpunkten die folgenden Varianten liefern - 120 Bildpunkte, Dateinamenszusatz `_tn` - 240 Bildpunkte, Dateinamenszusatz `_kl` - 500 Bildpunkte, Dateinamenszusatz `_sm` - 700 Bildpunkte, Dateinamenszusatz `_mt` - 1.200 Bildpunkte, Dateinamenszusatz `_gr` Wird also auf dem Server z.B. die Datei `Bild1.jpg` abgelegt, liefert der Server mit Aufruf der Datei `Bild1_sm.jpg` die Variante mit 500 Bildpunkten entlang der längsten Kante, mit Aufruf von `Bild1_mt.jpg` die Version mit 700 Bildpunkten usw. Auf dem Server wirken dafür Funktionen, die den Umgang mit verschiedenen Fassungen eines Bildes erleichtern und beispielsweise dafür sorgen, dass die für die verschiedenen Größen eines Bildes erforderlichen Fassungen automatisch erstellt und nachgehalten werden. Diese Funktionen übernimmt gewöhnlich ein [Content Management System (CMS)](https://de.wikipedia.org/wiki/Content-Management-System). Wie dieser Beitrag in der Folge zeigt, lässt sich einiges aber auch ohne großes CMS erreichen. ## Server für Bilder Zur Verwaltung von Webinhalten gibt es zahlreiche fertige Content Management Systeme. Der Umgang mit Bildern ist aber noch kein Anlaß, allein dafür schon ein großes CMS zu betreiben. 'Bordmittel' vorhandener Werkzeuge und ein paar zusätzliche Handgriffe reichen aus um ohne die Abhängigkeit von einem bestimmten und meistenteils sperrigen und großen System auszukommen. Als Ablaufumgebung werden für die in diesem Betrag geschilderten Methoden die [Java-Technologie](https://de.wikipedia.org/wiki/Java-Technologie) sowie der Web- und Applikationsserver [Tomcat](https://tomcat.apache.org) verwendet. ### Grundausstattung Wie jeder Webserver liefert Tomcat alle Inhalte eines entsprechend vorgegebenen Ordners über `http(s)` aus. Hierzu wird einfach eine XML-Datei im Ordner `$CATALINA_BASE/conf/Catalina/localhost` von Tomcat hinterlegt: ``` <?xml version="1.0" encoding="UTF-8"?> <Context path="/data" docBase="/pfad/zu/webinhalten" /> ``` Mit obiger Vorgehensweise liefert Tomcat alles per `http(s)` aus, was im betreffenden Ordner abgelegt ist, auch Bilder aller Art. Angenommen, unser Tomcat läuft unter der Domain `example.com` und es liegen auf dem Server Bilder also z.B. im Ordner `/pfad/zu/webinhalten/bilder` dann führt obige Konfiguration dazu, dass die betreffenden Bilder über `https://example.com/data/bilder` zugänglich sind. Den oben mit `/pfad/zu/webinhalten` angegebenen Ordner wollen wir `www-Ordner` nennen. Der Minimalansatz zur Herstellung der hier geschilderten Lösung wäre also einfach von Hand für jedes Bild unterschiedlich große Fassungen zu erzeugen und im `www-Ordner` abzulegen. ### Bildvarianten automatisch erzeugen Freilich lässt sich die Herstellung von Minituransichten und Zwischengrößen für ein Bild automatisieren. Tomcat liefert mit der Klasse `DefaultServlet` die Stelle, an der in die Auslieferung von Daten eingegriffen werden kann. Für unseren Fall soll immer dann, wenn eine Bilddatei ausgeliefert werden soll, geprüft werden, ob für die betreffende Datei eine passende Größe vorliegt. Wenn nicht soll diese Bilddatei erzeugt werden. Der folgende Code für `TNServlet` bewirkt die entsprechende Erweiterung des `DefaultServlet`: ``` public class TNServlet extends DefaultServlet { private static final Logger logger = Logger.getLogger(TNServlet.class.getName()); public static final String TN = "_tn"; // 120 public static final String KL = "_kl"; // 240 public static final String SM = "_sm"; // 500 public static final String MT = "_mt"; // 700 public static final String GR = "_gr"; // 1200 public static final String JPG = ".jpg"; public static final String JPEG = ".jpeg"; public static final String PNG = ".png"; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uriStr = request.getRequestURI(); String relname = uriStr.substring(request.getContextPath().length()); if(uriStr.endsWith(JPG) || uriStr.endsWith(JPEG) || uriStr.endsWith(PNG)) { File dir = new File(request.getServletContext().getRealPath("/")); String suburiStr = uriStr.substring(request.getContextPath().length()); File imgfile = new File(dir, suburiStr); if(uriStr.contains(TN)) { bildErzeugen(dir, relname, TN, 120, imgfile); } else if(uriStr.contains(KL)) { bildErzeugen(dir, relname, KL, 240, imgfile); } else if(uriStr.contains(SM)) { bildErzeugen(dir, relname, SM, 500, imgfile); } else if(uriStr.contains(MT)) { bildErzeugen(dir, relname, MT, 700, imgfile); } else if(uriStr.contains(GR)) { bildErzeugen(dir, relname, GR, 1200, imgfile); } } super.doGet(request, response); } private void bildErzeugen(File dir, String relname, String indicator, int gr, File tnfile) throws UnsupportedEncodingException, IOException { relname = relname.replace(indicator, ""); File imgfile = new File(dir, URLDecoder.decode(relname, "utf-8")); if(imgfile.exists() && !tnfile.exists()) { Thumbnails.of(imgfile) .size(gr, gr) .keepAspectRatio(true) .outputQuality(0.7) .toFile(tnfile); } } } ``` Für die Erzeugung kleinerer Fassungen einer Bilddatei dient hierbei die Klassenbibliothek [Thumbnailator](https://github.com/coobird/thumbnailator), die einfach in den Ordner `$CATALINA_BASE/lib` von Tomcat gelegt wird. Das obige `TNServlet` wird aktiviert, indem seine Java-Klasse im Ordner `$CATALINA_BASE/lib` von Tomcat hinterlegt und in einem Deployment Descriptor vermerkt wird, der unter `www-Ordner/WEB-INF/web.xml` abgelegt ist. Der Deployment Descriptor wird wie folgt erweitert: ``` <servlet> <servlet-name>TNServlet</servlet-name> <servlet-class>de.uhilger.wbx.web.TNServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TNServlet</servlet-name> <url-pattern>*.png</url-pattern> <url-pattern>*.gif</url-pattern> <url-pattern>*.jpeg</url-pattern> <url-pattern>*.jpg</url-pattern> </servlet-mapping> ``` Jeder Aufruf eines URL, der mit `jpg`, `jpeg`, `gif` oder `png` endet, wird über das `TNServlet` geleitet. Wird also beispielsweise der URL `https://example.com/data/bilder/name-des-bildes_tn.jpg` aufgerufen, prüft das `TNServlet`, ob im Ordner `bilder` die Datei `name-des-bildes_tn.jpg` existiert. Wenn nicht, wird von der Orignaldatei `name-des-bildes.jpg` eine neue Fassung mit 120 Bildpunkten entlang der längsten Kante erzeugt und im Ordner `bilder` unter dem Namen `name-des-bildes_tn.jpg` gespeichert und als Antwort auf den Aufruf des URL ausgeliefert. Für alle weiteren Male, wenn der URL aufgerufen wird, ist die Datei dann bereits vorhanden und muss nur noch ausgeliefert werden. ### Bildvarianten verbergen Mit dem `TNServlet` aus dem vorigen Abschnitt ist Tomcat bereits um den wesentlichen Baustein für den Umgang mit verschiedenen Fassungen pro Bild erweitert. Damit Webanwendungen die Bilder verwenden können fehlt allerdings noch eine Methode, die einer Webanwendung sagt, wo welche Bilder abgelegt sind. Im einfachsten Fall ist das eine Methode, die den Inhalt eines Ordners auflistet. Eine solche Methode kann auf dem Server an ganz unterschiedlicher Stelle realisiert und untergebracht werden. Eingesetzt wird dafür in aller Regel die Methode `listFiles` der Klasse `File` in der Variante mit `FileFilter`. ``` File dir = new File("/pfad/zum/ordner"); File[] fileArray = dir.listFiles(new ImgFileFilter()); ``` Der obige Aufruf ermittelt die Dateien in einem Ordner und verwendet einen Filter, der bestimmte Dateien auslässt. Ein solcher Filter sieht für unsere Zwecke wie folgt aus: ``` public class ImgFileFilter implements FileFilter { @Override public boolean accept(File pathname) { boolean pass = true; String fname = pathname.getName().toLowerCase(); if (fname.endsWith(TNServlet.JPEG) || fname.endsWith(TNServlet.JPG) || fname.endsWith(TNServlet.PNG)) { if (fname.contains(TNServlet.GR) || fname.contains(TNServlet.KL) || fname.contains(TNServlet.MT) || fname.contains(TNServlet.SM) || fname.contains(TNServlet.TN)) { pass = false; } } return pass; } } ``` Der `ImgFileFilter` verbirgt Dateien, die auf `jpg`, `jpeg` oder `png` enden und deren Name mit `_tn`, `_kl`, `_sm`, `_mt` oder `_gr` endet. Die Listenfunktion liefert also stets die Originalbilddatei aus, auch, wenn im betreffenden Ordner noch andere Varianten dieser Datei liegen. Eine solche Dateiliste kann eine Webanwendung nutzen, um z.B. eine Übersicht aller Bilder einer Sammlung oder eines Ordners vom Server abzurufen. Die so gewonnene Liste kann anschließend aufseiten der Webanwendung genutzt werden, um ganz nach Bedarf Bilder in eine Webseite einzubauen. Soll die Miniaturansicht (120 Bildpunkte) dargestellt werden, wird dem URL zum Originalbild einfach die Endung `_tn` angefügt. Eine mittelgroße Variante mit 500 Bildpunkten erhält die Endung `_sm` usw. Es genügt, die Originaldatei auf dem Server zu hinterlegen. Das `TNServlet` sorgt automatisch für die Lieferung der richtigen Größe und erzeugt diese nötigenfalls selbsttätig. ### Löschen und Umbenennen Eine Anwendung zur Verwaltung der auf dem Server befindlichen Dateien muss um Methoden erweitert werden, die mit den zusätzlichen Bilddateien im Fall des Löschens oder Umbenennens umgehen können. Wie in diesen Fällen verfahren werden kann ist nachfolgend am Beispiel Löschen gezeigt. Wird eine Bilddatei zum Löschen ausgewählt und dem Server programmatisch ein Aufruf zum Löschen dieser Datei gesendet, muss die Methode zum Löschen prüfen, ob zur Bilddatei noch weitere Varianten erzeugt wurden und diese mitlöschen wie im folgenden Beispiel: ``` String fname = targetFile.getName().toLowerCase(); if(fname.endsWith(TNServlet.JPEG) || fname.endsWith(TNServlet.JPG) || fname.endsWith(TNServlet.PNG)) { deleteImgFiles(targetDir, targetFile); } else { targetFile.delete(); } ``` Die Methode `deleteImgFiles` aus obigem Beispiel sorgt dann für das Löschen aller Bildvarianten indem für einen gegebenen Ordner und eine zu löschende Datei ein Ausdruck `[Ordner]/[Dateiname]*` verwendet wird. Der Platzhalter `*` findet alle Dateien die so heißen wie die Originalbilddatei, auch die Varianten, die mit `_tn`, `_kl` usw. enden und bewirkt, dass diese mit gelöscht werden: ``` public void deleteImgFiles(File targetDir, File targetFile) { String fnameext = targetFile.getName(); int dotpos = fnameext.lastIndexOf("."); String fname = fnameext.substring(0, dotpos); String ext = fnameext.substring(dotpos); FileFilter fileFilter = new WildcardFileFilter(fname + "*" + ext); File[] files = targetDir.listFiles(fileFilter); for (int i = 0; i < files.length; i++) { files[i].delete(); } } ``` Die Klasse `WildcardFileFilter` vereinfacht hierbei die Bestimmung der gesuchten Dateien erheblich und wird von der Klassenbibliothek [Apache Commons IO](http://commons.apache.org/proper/commons-io/) geliefert. Diese Klassenbibliothek enthält neben vielen anderen nützlichen Helfern auch die Klasse `FileUtils` mit der serverseitig alle Dateioperationen ausgeführt werden können, die von einer Dateiverwaltung benötigt werden. ## Hilfsmittel Abschließend sind nochmals die Hilfsmittel zusammengefasst, mit denen der gewünschte Umgang mit Bildern im Web erzielt wird: - [Java](https://openjdk.java.net/) - [Tomcat](https://tomcat.apache.org) - [Apache Commons IO](http://commons.apache.org/proper/commons-io/) - [Thumbnailator](https://github.com/coobird/thumbnailator) Die Komponenten sind unter den angegebenen Verweisen als freie Software erhältlich. ## Schlussbemerkungen Damit Webanwendungen nicht von der Größe und Vielzahl von Bildern ausgebremst werden bedarf es einiger Vorarbeit. Dieser Beitrag hat zusammengefasst, worauf zu achten ist und zeigt, dass die meisten Hilfsmittel auf dem Weg zu schnellen bebilderten Webseiten bereits vorhanden sind. Mit wenigen Zeilen Code ist das gewünschte Verhalten individuell ins passende Werk gesetzt. Einschränkungen durch aufwendige Content Management Systeme oder durch vorgefertigte Webseitenbaukästen ohne die gebotene Flexibilität können auf diese Weise wirksam vermieden werden.