Johannes Ganzenmüller

Prozessautomatisierung mit Gulp

Veröffentlichungsdatum Lesezeit 7 Minuten zum Lesen

Mit der Umstellung der Homepage auf Jekyll gab es einige Dinge "neu zu erfinden", die zuvor bei Wordpress im Standard mitgeliefert worden sind oder wofür es Plugins gab. Ein Thema, dass es zu lösen gab, war das Erstellen von Thumbnails bzw. die Konvertierung der Bilder in kleinere Varianten, damit die einzelnen Seiten schneller laden.

Eine Möglichkeit wäre es gewesen, die Bilder bei einem speziellen Provider wie z.B. cloudinary hosten zu lassen, sodass ich einfach Varianten in diversen Bildergrößen anfragen hätte können. Da ich die Seite oder Teile davon aber nicht bei zwei verschiedenen Diensten hochladen wollte, hab ich einen anderen Weg gesucht, der komplett ohne einen weiteren Dienst auskommt und somit etwas einfacher ist. Nach etwas Recherche bin ich auf Gulp gestoßen, was an sich ganz spannend und nach dem richtigen Werkzeug aussah, um wiederkehrende Prozesse zu automatisieren. Also hab ich mich darin etwas eingelesen, die nötigen Pakete auf meinem Rechner installiert und ziemlich schnell war auch die erste Variante meines Skripts fertig, aber fangen wir am Anfang an.

Ausgangslage

Ich hab Versionen von meinen Bildern in größeren Auflösungen vorliegen, die dementsprechend auch relativ wie Speicherplatz benötigen und damit auf einer Galerie-Seite für das Web nicht geeignet sind. Daher sollen aus diesen Bildern kleinere Varianten zu Vorschauzwecken zu generiert werden. Weil das eine Aufgabe ist, die jedes Mal beim Hochladen von neuen Bilder anfällt, soll es entsprechend einfach und schnell gehen.

Umsetzung

Der Aufbau eines Gulp-Skripts ist recht einfach: Zuerst wird definiert welche Dateien verarbeitet werden, dann die Logik was gemacht und am Ende wohin das Ergebnis ausgegeben werden soll. Zugegebener Weise war ich nicht der Erste, der Gulp für diesen Anwendungsfall benutzt, sodass sich im Netz etliche Seiten, Skripte und Bibliotheken finden, die man sich anschauen und von denen man sich bedienen kann. Die Kunst ist es aber zu wissen, was man will und worin sich sein Anwendungsfall von dem Anwendungsfall unterscheidet, den man sich gerade anschaut. ;)

Bei den Bildern sieht meine Dateistruktur aktuell in etwa so aus:

./assets/photography
├── /2009_02_irland
├── /2009_09_israel
├── ...

Meine erste Überlegung war die Vorschaubilder (=Thumbnails) in einem Unterordner bei den großen Versionen abzulegen. Das Ergebnis hätte in etwa folgend ausgesehen:

./assets/photography
├── /2009_02_irland
│   └── /thumbnails
├── /2009_09_israel
│   └── /thumbnails
├── ...

Nach einigen Versuchen und einer anschließenden Pause um nochmal nachzudenken hab ich mich dann aber doch für eine andere Struktur entscheiden, denn Gulp scheint mir dafür ausgelegt zu sein getrennte Eingangs- und Ausgangsverzeichnisse zu haben. Und am Ende macht es für die Homepage und mich keinen Unterschied wo die Dateien liegen, Hauptsache sie folgen einer definierten Struktur.

Die finale Struktur sieht wie folgt aus:

./
├── /assets/photography
│   └── /2009_02_irland
│   └── /2009_09_israel
│   └── ...
├── /thumbnails/photography
│   └── /2009_02_irland
│   └── /2009_09_israel
│   └── ...
├── ...

Schlussendlich gefällt mir die Variante sogar besser, weil, sollte ich tatsächlich mal auf einen der Bild-Hosting-Dienste umstellen wollen, hab ich deutlich weniger Aufwand die Vorschaubilder wieder von den Ausgangsdaten zu trennen.

Skript

Hier der Code, der herausgekommen ist:

var gulp = require('gulp');
var imageResize = require('gulp-image-resize');
var imagemin = require('gulp-imagemin');
var changed = require('gulp-changed');

gulp.task('default', function () {
  return gulp.src([
    'assets/**/*',
    '!assets/css/**',
    '!assets/images/**'
    ])
    .pipe(changed('thumbnails'))
    .pipe(imageResize({
      width: 400,
      height: 400,
      crop: false,
      upscale: false
    }))
    .pipe(imagemin())
    .pipe(gulp.dest('thumbnails'));
});
  1. Zu Beginn werden mit require einige Bibliotheken als Abhängigkeiten definiert und dann startet auch schon der task, der die Verarbeitung übernimmt.
  2. gulp.src sagt welche Dateien bearbeitet werden sollen, in meinem Fall alles im assets-Ordner mit Ausnahme von assets/css und assets/images. Die anschließenden pipe-Befehle entsprechen je einem Verarbeitungsschritt.
  3. Mit changed wird geschaut, ob die Eingangsdaten schon im Verzeichnis thumbnails vorhanden sind, und wenn dass der Fall ist, dann werden diese ignoriert. So werden nur die Dateien verarbeitet, die neu sind oder sich geändert haben, was die Ausführungszeit des Skriptes stark reduziert. Dass war der Teil der am meisten Schwierigkeiten mit den vorherigen Ordnerstrukturen bereitet hat.
  4. In imageResize passiert die meiste Magie, den diese Zeile ist für die Erstellung der kleineren Bilder zuständig. In meinem Fall hab ich mit width und heigth definiert, dass die längste Seite des Bilder 400 Pixel sein soll. upscale: false verhindert dabei, dass kleinere Bilder vergrößert werden und crop: false besagt, dass das Seitenverhältnis beibehalten werden soll. Bei crop: true würde ich quadratische Vorschaubilder im Format 400x400 bekommen, die ein Ausschnitt des größeren Bildes sind, was ich aber jedoch nicht will.
  5. imagemin komprimiert die Bilder noch etwas, sodass die Dateigröße noch etwas kleiner wird. Natürlich ohne die Qualität des Bildes zu beeinträchtigen.
  6. gulp.dest definiert das Ausgabeverzeichnis, wobei der bisherige Pfad eines Bildes erhalten bleibt, sodass nicht alle Bilder direkt im thumbnails-Ordner sind, sondern ihre bisherige Ordnerstruktur beibehalten.

Immer wenn ich jetzt neue Bilder einfüge oder bestehende ändere, führe ich einfach den Befehl gulp auf der Kommandozeile aus und die neuen Vorschaubilder werden innerhalb von ein paar Sekunden erzeugt :)

Ergebnis

Ich bin mit der Variante sehr zufrieden und es hat auch Spaß gemacht mit Gulp zu experimentieren. Das Skript ist recht simpel und die Laufzeit ist, mit ein paar Sekunden, relativ gering, besonders wenn es nur neue Bilder verarbeiten muss.

Aber ich hatte das Ganze ja gemacht, um die Ladezeit der Seite zu verkürzen und daher hier ein paar Daten, um das Ergebnis zu bewerten. Für die Performance-Messungen habe ich Pingdom mit dem Standort "Europe - Germany - Frankfurt" genutzt und mehrere Versuche mit etwas Zeitabstand durchgeführt.