Direkt zum Hauptinhalt

Optimierung von Bilddateien

Im Wesentlichen werden zwei Bildformate verwendet, wobei jedes Format für bestimmte Inhalte optimiert ist:

  • PNG: Geeignet für Screenshots, Grafiken und Motive mit wenigen, flächigen Farben
  • JPEG: Geeignet für natürliche Inhalte wie Fotos und Bilder mit komplexen Strukturen, Farben und Farbverläufen

Meist werden diese Bilder nicht im für Webseiten optimalen Format gespeichert, d. h.

  • in PNGs wird die komplette Farbpalette (RGB + Transparenz, mit 24 bzw. 32 bit) verwendet, obwohl für Darstellung deutlich weniger Farben ausreichen
  • JPEGs werden qualitativ sehr hochwertig erstellt, erstellt Dateien sind recht groß
  • JPEGs werden im Format Baseline erstellt, können also nicht progressiv geladen werden
  • Bilder enthalten Metadaten

Hier einige Referenzen zur Optimierung von Bildern für die Darstellung auf Webseiten:

Bilder in Bookstack automatisch optimieren

Um Nutzer nicht unnötig mit der Optimierung zu belasten, optimiert nachfolgendes Skript Bilddateien automatisch. Dabei kommen pngquant und jpegoptim zum Einsatz, welche für gängige Linux-Distributionen angeboten werden. Täglich 1x werden die Bilder aller Unterverzeichnisse (cover_book, cover_bookshelf, drawio, gallery, system, user) des aktuellen Monats im Image-Ordner bearbeitet.

Verzeichnis: /var/www/bookstack/public/uploads/images/[asset]/[YYYY-MM]/

Mit jedem Durchlauf werden Bilder weiter optimiert, wobei die Optimierung beim ersten Durchlauf am größten ist. Pngquant und jpegoptim optimieren die Bilder nur, wenn eine Mindestqualität nicht unterschritten wird. Die mehrfache Anwendung des Skriptes verschlechtert die Bildqualität daher nicht. Nach 2 bis 3 Durchläufen ist das angestrebte Optimum erreicht. In Tests mit mehreren hundert Bildern wurden zwischen 66 und 80 % der Dateigröße ohne sichtbaren Qualitätsverlust eingespart. Bei Dateien die mit in Bookstack integriertem draw.io erstellt/bearbeitet wurden und welche im Ordner drawio liegen, werden die Metadaten beibehalten, da diese die Vektorgrafiken für spätere Bearbeitung enthalten. Würden sie entfernt, könnten sie nicht mehr mit draw.io bearbeitet werden.

Benötigte Komponenten installieren:

apt-get update
apt-get install pngquant
apt-get install jpegoptim

Skript installieren

/usr/local/bin/bs-opt-img.sh

# !/bin/bash
# set -x
#
# Recompresses uploaded images of the current month and strips meta tags.
# Images are only processed if there are potential savings. Script can be run
# multiple times without recompressing already compressed images - processed
# images are skipped.Needs pngqunat and jpegoptimum to be installed. Run script
# atleast once a week better daily.
# Logs at /var/log/[script-name].log
# Metadata of png files in drawio are not stripped because they are containing
# vector data which are neccessary to edit these drawings
#
# Version 2023-10-25
# daniel.obst@uni-leipzig.de

FOLDER_BASE=/var/www/bookstack/public/uploads/images

LOG=/var/log/`echo $(basename "$0") | rev | cut -d. -f2- | rev`.log
#FOLDERS="cover_book cover_bookshelf drawio gallery system user"
echo "$(date '+%Y-%m-%d %H:%M:%S') === START ===" >> $LOG
FOLDERS="`ls -q $FOLDER_BASE`"
for FOLDER in $FOLDERS; do
  FOLDER_LONG=$FOLDER_BASE/$FOLDER/`date +"%Y-%m"`/
# FOLDER_LONG=$FOLDER_BASE/$i/2023-07/
  if [ -d "$FOLDER_LONG" ]; then
    DU_BEFORE=`du -s --block-size=1 $FOLDER_LONG | cut -f1`
    DU_BEFORE_K=$(($DU_BEFORE/1024))
    FILES=`ls  -lq $FOLDER_LONG | grep -iE ".*\.(jpg|jpeg|png)$" | wc -l`
    if [ $FILES -gt 0 ]; then
      if [ "$FOLDER" = "drawio" ]; then
        find $FOLDER_LONG -name "*.png" -exec pngquant --force --skip-if-larger --speed=1 --ext .png --quality=60-80 {} \;
        find $FOLDER_LONG -name "*.PNG" -exec pngquant --force --skip-if-larger --speed=1 --ext .PNG --quality=60-80 {} \;
      else
        find $FOLDER_LONG -name "*.png" -exec pngquant --strip --force --skip-if-larger --speed=1 --ext .png --quality=60-80 {} \;
        find $FOLDER_LONG -name "*.PNG" -exec pngquant --strip --force --skip-if-larger --speed=1 --ext .PNG --quality=60-80 {} \;
      fi
      find $FOLDER_LONG -iname "*.png" -exec chown -f --reference=$FOLDER_LONG {} \;
      find $FOLDER_LONG -iregex ".*\.jpe?g" -exec jpegoptim -q -p --all-progressive -m75 --strip-com --strip-exif --strip-iptc --strip-icc --strip-xmp {} \;
      DU_AFTER=`du -s --block-size=1 $FOLDER_LONG | cut -f1`
      DU_AFTER_K=$((DU_AFTER/1024))
      if [ $DU_AFTER -lt $DU_BEFORE ]; then
        SAVED_K=$((DU_BEFORE_K-$DU_AFTER_K))
        echo "$(date '+%Y-%m-%d %H:%M:%S') $FOLDER_LONG $FILES images, folder shrinked from ${DU_BEFORE_K}k to ${DU_AFTER_K}k, ${SAVED_K}k saved" >> $LOG
      else
        echo "$(date '+%Y-%m-%d %H:%M:%S') $FOLDER_LONG nothing to do, $FILES images, ${DU_AFTER_K}k" >> $LOG
     fi
    else
      echo "$(date '+%Y-%m-%d %H:%M:%S') $FOLDER_LONG no images found" >> $LOG
    fi
  else
    echo "$(date '+%Y-%m-%d %H:%M:%S') $FOLDER_LONG does not exists" >> $LOG
  fi
done
echo "$(date '+%Y-%m-%d %H:%M:%S') $FOLDER_BASE/ `find $FOLDER_BASE/. -type f | wc -l` files, `du -sh $FOLDER_BASE | cut -f1`" >> $LOG
echo "$(date '+%Y-%m-%d %H:%M:%S') === STOP ===" >> $LOG

Cronjob

mit crontab -e

0 23 * * * /usr/local/bin/bs-opt-img.sh

Logs

Das Skript schreibt Logs nach /var/log/bs-opt-img.log. Um zu verhindern, dass die Logs die Festplatte vollschreiben, sollten die Logs rotiert werden. Dazu eine Datei /etc/logrotate.d/bs-opt-img mit folgendem Inhalt anlegen:

/var/log/bs-opt-img.log
{
        rotate 4
        weekly
        missingok
        notifempty
        compress
        delaycompress
        sharedscripts
}

Syslog Daemon neustarten:

systemctl restart rsyslog.service