Status: Geplant · Schwierigkeit: Fortgeschritten · Werkzeuge: n8n, Claude API (Anthropic), lokales Dateisystem

Die Idee

↑ Zur Kapitelübersicht

Nach jedem Fotoausflug dasselbe: hunderte Dateien mit nichtssagenden Namen wie DSC_4721.jpg landen im Import-Ordner und warten darauf, sortiert und benannt zu werden.

Das manuelle Durchsehen, Umbenennen und Einsortieren kostet mehr Zeit als der Ausflug selbst. Die meisten Fotos folgen dabei einem klaren Muster: Datum, Ort, Motiv – das reicht für einen sinnvollen Dateinamen und eine logische Ordnerstruktur.

Zwei dieser drei Informationen stecken bereits im Foto selbst: Das Datum im EXIF-Header, den Ort eventuell als GPS-Koordinate. Das Motiv – was auf dem Bild zu sehen ist – kann seit dem Aufkommen multimodaler KI-Modelle automatisch beschrieben werden. Ein n8n-Workflow kann das alles zusammenführen.

Vorher:
DSC_4721.jpg im Ordner Import/

Nachher:
2026-04-15_hamburg-alster_spaziergang-mit-hund.jpg
im Ordner Fotos/2026/04_April/Hamburg/

Was das Projekt können soll

↑ Zur Kapitelübersicht

Klare Anforderungen – und bewusste Nicht-Ziele.

Muss-Anforderungen

✓ Neue Dateien im Import-Ordner automatisch erkennen.
✓ Aufnahmedatum aus EXIF-Metadaten auslesen.
✓ Bildinhalt per KI beschreiben lassen.
✓ Neuen Dateinamen aus Datum und Beschreibung erzeugen.
✓ Datei in Unterordner nach Jahr und Monat sortieren.
✓ Nur JPEG und RAW-Dateien verarbeiten, andere ignorieren.

Bewusst nicht enthalten

✗ Kein automatisches Löschen von Duplikaten.
✗ Keine Bildbearbeitung oder Komprimierung.
✗ Keine Gesichtserkennung oder Personenmarkierung.
✗ Keine Cloud-Synchronisation.
✗ Keine Änderung von RAW-Dateien – nur Umbenennen.

Die Lösung im Überblick

↑ Zur Kapitelübersicht

Fünf Schritte vom Rohdatei-Import bis zur sortierten Bibliothek.

Workflow-Ablauf:

[Watch Folder Trigger] neue Datei im Import-Ordner
  ↓
[Code Node] EXIF-Metadaten auslesen
  ↓ Datum, Kamera, GPS
[HTTP Request] Bild an Claude API senden
  ↓ Bildbeschreibung als Text
[Code Node] Dateinamen + Zielpfad erzeugen
  ↓
[Move/Rename File] Datei umbenennen und verschieben

Warum ein Workflow und kein Agent?

Der Ablauf ist für jedes Foto identisch: Metadaten lesen → KI fragen → umbenennen → verschieben. Es gibt keine offenen Entscheidungen, keine Rückfragen und keine variablen Schritte. Ein fester Workflow ist hier die richtige Wahl – zuverlässiger, günstiger und leichter zu debuggen als ein Agent. Die KI übernimmt nur einen klar abgegrenzten Schritt: das Beschreiben des Bildinhalts.

Warum Claude für die Bildbeschreibung?

Claude Haiku unterstützt multimodale Eingaben – also Text und Bild in einer Anfrage. Es beschreibt Bildinhalte präzise, auf Deutsch und in kompakter Form, was direkt als Dateinamen-Bestandteil verwendbar ist. Alternativ funktioniert auch GPT-4o mit sehr ähnlicher Qualität.

Voraussetzungen

↑ Zur Kapitelübersicht

Was bereit sein muss, bevor der erste Node gesetzt wird.

n8n-Instanz mit Dateisystem-Zugriff

Dieser Workflow greift auf lokale Ordner zu – das funktioniert nur mit der Desktop-App oder einer Docker-Instanz auf dem eigenen Rechner. n8n Cloud hat keinen Zugriff auf das lokale Dateisystem.
n8n installieren

Zwei Ordner anlegen

Import-Ordner: Hierhin werden neue Fotos von der Kamera kopiert. n8n überwacht diesen Ordner.
Beispiel: C:\Fotos\Import\ bzw. /home/benutzer/Fotos/Import/

Ziel-Ordner: Hier entstehen die sortierten Unterordner.
Beispiel: C:\Fotos\Archiv\ bzw. /home/benutzer/Fotos/Archiv/

Anthropic API-Key (Claude)

Für die Bildbeschreibung wird ein multimodales Modell benötigt – Claude Haiku unterstützt Bilder und ist kostengünstig (unter 1 Cent pro Bild bei typischen Urlaubsfotos).
console.anthropic.com
API-Keys verwalten

exiftool installiert (empfohlen)

Für das zuverlässige Auslesen von EXIF-Metadaten ist exiftool das beste Werkzeug. Kostenlos und für Windows, macOS und Linux verfügbar:
exiftool.org

Alternativ kann der n8n Code-Node EXIF-Daten über das npm-Paket exifr auslesen – ohne externe Installation.

Schritt 1: Trigger – neues Foto erkannt

↑ Zur Kapitelübersicht

Der Watch Folder Trigger überwacht den Import-Ordner und startet den Workflow bei jeder neuen Datei sofort.

Node-Konfiguration

Node: Local File Trigger (unter n8n-nodes-base)
Name: „Neues Foto erkannt"

Trigger On: File Added

Path: Vollständiger Pfad zum Import-Ordner, z.B. /home/benutzer/Fotos/Import

Recursive: Aus (nur der direkte Ordnerinhalt, keine Unterordner)

Ignore: **/*.db, **/*.ini, **/Thumbs.db
(Systemdateien überspringen)

Dateityp-Filter

Nach dem Trigger einen IF-Node einfügen, der nur Bilddateien durchlässt:

Value 1 (Expression):
{{ $json.name.toLowerCase().match(/\.(jpg|jpeg|raw|cr2|nef|arw)$/) ? true : false }}
Operation: Equal
Value 2: true

Andere Dateitypen (z.B. Videos oder Textdateien) werden damit stillschweigend ignoriert.

Schritt 2: Metadaten auslesen

↑ Zur Kapitelübersicht

Ein Code-Node liest Datum, Kamera und GPS-Koordinaten direkt aus dem EXIF-Header der Bilddatei.

Node-Konfiguration

Node: Code
Name: „EXIF auslesen"
Language: JavaScript

Code
const { execSync } = require('child_process');
const path = require('path');

const filePath = $json.path;
const fileName = $json.name;

// EXIF über exiftool auslesen (muss installiert sein)
let exifRaw = '';
try {
    exifRaw = execSync(
        `exiftool -json -DateTimeOriginal -Make -Model -GPSLatitude -GPSLongitude "${filePath}"`,
        { encoding: 'utf8' }
    );
} catch(e) {
    exifRaw = '[]';
}

const exif = JSON.parse(exifRaw)[0] ?? {};

// Datum parsen (Format: "2026:04:15 10:23:45")
let datumISO = '';
if (exif.DateTimeOriginal) {
    const parts = exif.DateTimeOriginal.split(' ');
    datumISO = parts[0].replace(/:/g, '-'); // "2026-04-15"
} else {
    // Fallback: Datei-Änderungsdatum
    datumISO = new Date($json.mtime).toISOString().split('T')[0];
}

const [jahr, monat] = datumISO.split('-');
const monatsNamen = ['Januar','Februar','März','April','Mai','Juni',
    'Juli','August','September','Oktober','November','Dezember'];
const monatsName = monatsNamen[parseInt(monat) - 1];

return [{
    json: {
        originalPath: filePath,
        originalName: fileName,
        datumISO,
        jahr,
        monat,
        monatsName,
        kamera: `${exif.Make ?? ''} ${exif.Model ?? ''}`.trim(),
        gpsLat: exif.GPSLatitude ?? null,
        gpsLng: exif.GPSLongitude ?? null,
    }
}];

Schritt 3: KI beschreibt das Bild

↑ Zur Kapitelübersicht

Das Bild wird als Base64 an die Claude API gesendet. Das Modell beschreibt den Inhalt in 3–5 Wörtern – kompakt genug für einen Dateinamen.

Bild als Base64 einlesen

Vor dem API-Aufruf muss die Bilddatei in Base64 umgewandelt werden. Dafür einen weiteren Code-Node einfügen:

Node: Code
Name: „Bild kodieren"

const fs = require('fs');
const imageBuffer = fs.readFileSync($json.originalPath);
const base64 = imageBuffer.toString('base64');

// Dateityp bestimmen
const ext = $json.originalName.split('.').pop().toLowerCase();
const mimeType = ext === 'jpg' || ext === 'jpeg'
    ? 'image/jpeg'
    : 'image/png';

return [{ json: { ...$json, base64Image: base64, mimeType } }];
HTTP Request an Claude API

Node: HTTP Request
Name: „Claude: Bild beschreiben"
Method: POST
URL: https://api.anthropic.com/v1/messages

Headers:
x-api-key: Credential (Anthropic API-Key)
anthropic-version: 2023-06-01
content-type: application/json

Body (JSON, Expression):

{
  "model": "claude-haiku-4-5",
  "max_tokens": 60,
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "image",
          "source": {
            "type": "base64",
            "media_type": "{{ $json.mimeType }}",
            "data": "{{ $json.base64Image }}"
          }
        },
        {
          "type": "text",
          "text": "Beschreibe dieses Foto in maximal 5 deutschen Wörtern. Nur Substantive und Adjektive, durch Bindestriche verbunden, keine Großbuchstaben, keine Satzzeichen. Beispiel: sonnenuntergang-nordsee-weite oder stadtpark-spaziergang-herbst"
        }
      ]
    }
  ]
}
Beschreibung aus Antwort extrahieren

Die Antwort der API enthält den Text unter:
$json.content[0].text

Einen Code-Node zur Bereinigung anhängen – Leerzeichen entfernen, Sonderzeichen ersetzen:

const roh = $json.content[0]?.text ?? 'unbekannt';

// Bereinigen: nur Kleinbuchstaben, Bindestriche, keine Umlaute
const sauber = roh
    .toLowerCase()
    .trim()
    .replace(/ä/g, 'ae').replace(/ö/g, 'oe')
    .replace(/ü/g, 'ue').replace(/ß/g, 'ss')
    .replace(/[^a-z0-9\-]/g, '-')
    .replace(/-+/g, '-')
    .replace(/^-|-$/g, '')
    .substring(0, 50); // Maximallänge

return [{ json: { ...$input.first().json, beschreibung: sauber } }];

Hinweis zu RAW-Dateien:
Rohdateien (CR2, NEF, ARW) können nicht direkt als Base64 an die Claude API gesendet werden – das Modell erwartet JPEG oder PNG. Für RAW-Dateien den Schritt überspringen und nur mit Datum und Kameraname arbeiten, oder vorher mit dcraw oder LibRaw ein Vorschaubild extrahieren.

Schritt 4: Dateiname und Zielpfad erzeugen

↑ Zur Kapitelübersicht

Aus Datum und Bildbeschreibung entsteht der neue Dateiname und der Zielordner.

Node-Konfiguration

Node: Code
Name: „Dateiname bauen"

const basisPfad = '/home/benutzer/Fotos/Archiv'; // anpassen

const {
    datumISO, jahr, monat, monatsName,
    beschreibung, originalName
} = $json;

// Dateiendung beibehalten
const ext = originalName.split('.').pop().toLowerCase();

// Neuer Dateiname: 2026-04-15_alster-spaziergang-hund.jpg
const neuerName = `${datumISO}_${beschreibung}.${ext}`;

// Zielordner: /Archiv/2026/04_April/
const zielOrdner = `${basisPfad}/${jahr}/${monat}_${monatsName}`;

// Vollständiger Zielpfad
const zielPfad = `${zielOrdner}/${neuerName}`;

return [{
    json: {
        ...$json,
        neuerName,
        zielOrdner,
        zielPfad,
    }
}];

Namenskonflikte vermeiden:
Falls am selben Tag zwei Fotos denselben Beschreibungstext bekommen – was selten, aber möglich ist – entsteht ein Namenskonflikt. Einfache Lösung: den originalen Dateinamen ohne Erweiterung als eindeutigen Suffix anhängen:
2026-04-15_alster-spaziergang_DSC4721.jpg

Schritt 5: Datei umbenennen und sortieren

↑ Zur Kapitelübersicht

Zielordner anlegen falls nicht vorhanden, Datei verschieben, Original löschen.

Zielordner erstellen

Node: Code
Name: „Ordner anlegen"

const fs = require('fs');
fs.mkdirSync($json.zielOrdner, { recursive: true });
// recursive: true legt alle fehlenden Ebenen an
return [$input.first()];
Datei verschieben

Node: Move/Copy File (unter n8n-nodes-base)
Name: „Datei verschieben"

Operation: Move
Source Path (Expression): {{ $json.originalPath }}
Destination Path (Expression): {{ $json.zielPfad }}

Sicherheitsnetz: erst kopieren, dann prüfen:
Wer auf Nummer sicher gehen möchte, nutzt als Operation Copy statt Move. Die Originaldatei bleibt im Import-Ordner, bis manuell geprüft wurde dass alles korrekt sortiert ist. Erst dann werden die Originale gelöscht. Für die erste produktive Version empfohlen.

Workflow exportieren und importieren

↑ Zur Kapitelübersicht

Der vollständige Workflow als JSON-Datei zum Download.

Nach dem Import anpassen

1. Import-Ordner-Pfad im Local File Trigger anpassen.
2. Basis-Pfad im Code-Node „Dateiname bauen" anpassen.
3. Anthropic Credential hinterlegen.
4. Erst mit einer Kopie testen – niemals direkt mit dem Original-Fotoarchiv.
5. Wenn alles korrekt sortiert wird, von Copy auf Move umstellen.

Workflow-JSON

Alternativ zum Download: Inhalt kopieren und in n8n über Workflows → Import from clipboard einfügen.


                    

Sicherheit & Datenschutz

↑ Zur Kapitelübersicht

Bilder enthalten persönliche Daten – das verdient besondere Sorgfalt.

Bilder werden an Anthropic gesendet

Beim Beschreibungsschritt wird das Bild als Base64-Daten an die Claude API übertragen. Anthropic verarbeitet diese Daten zur Beantwortung der Anfrage; nach Angaben von Anthropic werden API-Eingaben nicht für das Training verwendet. Wer private oder sensible Fotos hat, sollte diesen Schritt für solche Bilder deaktivieren oder den Filter auf „nur Landschaften" im System-Prompt einschränken.

Alternative: lokales Modell

Wer keine Bilder an externe Dienste senden möchte, kann stattdessen ein lokales multimodales Modell über Ollama betreiben – z.B. LLaVA oder MiniCPM-V. n8n kann lokale Ollama-Modelle über den Ollama Chat Model-Node ansprechen. Die Beschreibungsqualität ist etwas geringer, aber alle Daten bleiben auf dem eigenen Rechner.

Kein unbeabsichtigtes Löschen

Der Workflow verschiebt Dateien – er löscht sie nicht. Im schlimmsten Fall landet ein Bild im falschen Ordner, ist aber nicht weg. Trotzdem: Vor dem ersten produktiven Einsatz immer mit einem Test-Ordner mit Kopien arbeiten.

Was ich gelernt habe

↑ Zur Kapitelübersicht

Ehrliches Fazit nach der Planung und ersten Tests.

✅ Hat gut funktioniert

Die Bildbeschreibungen von Claude Haiku sind für Dateinamen-Zwecke erstaunlich brauchbar. „sonnenuntergang-elbe-boote" oder „stadtpark-herbst-laub" treffen den Inhalt präzise genug, um das richtige Foto später wiederzufinden. Der Prompt mit explizitem Format-Beispiel (Wörter durch Bindestriche) funktioniert zuverlässig.

⚠️ Hat länger gedauert als gedacht

Das Base64-Encoding großer Bilder erzeugt sehr lange Strings – ein 10-MB-RAW-Foto als Base64 überschreitet das Kontextfenster des Modells. Lösung: JPEGs vor dem API-Aufruf auf maximal 2 MB komprimieren, z.B. mit dem sharp-Paket im Code-Node.

❌ Sackgasse

Der Local File Trigger in n8n meldet manchmal dieselbe Datei mehrfach, wenn das Kopieren länger dauert als erwartet (z.B. bei großen RAW-Dateien über USB). Eine einfache Lösung ist das Führen einer JSON-Datei mit bereits verarbeiteten Dateinamen – der Workflow prüft zu Beginn ob der Name bereits enthalten ist und überspringt ihn ggf.

Troubleshooting

↑ Zur Kapitelübersicht

Die häufigsten Probleme beim Aufbau dieses Projekts.

Problem: exiftool nicht gefunden

Ursache: exiftool ist nicht im Systempfad.
Lösung Windows: Vollständigen Pfad im Code angeben, z.B. C:\\exiftool\\exiftool.exe
Alternative: npm-Paket exifr im Code-Node verwenden – keine externe Installation nötig.

Problem: Claude-Antwort enthält Sonderzeichen oder Großbuchstaben

Ursache: Das Modell hält sich nicht immer exakt an das Format-Beispiel im Prompt.
Lösung: Den Bereinigungsschritt nach der API-Antwort robust gestalten – der Code-Node in Schritt 3 normalisiert bereits Umlaute und Sonderzeichen. Zusätzlich im Prompt expliziter werden: „Antworte ausschließlich mit Kleinbuchstaben und Bindestrichen, ohne Leerzeichen."

Problem: Datei wird mehrfach verarbeitet

Ursache: Trigger feuert mehrmals während die Datei noch geschrieben wird.
Lösung: Einen Wait-Node (2–3 Sekunden) direkt nach dem Trigger einfügen. So ist die Datei vollständig geschrieben, bevor der Workflow sie liest.

Problem: Zielordner wird nicht angelegt

Ursache: n8n hat keine Schreibrechte im Zielverzeichnis.
Lösung: Berechtigungen des Zielordners prüfen. Unter Linux/macOS: chmod 755 /pfad/zum/archiv oder den Ordner als Benutzer anlegen, unter dem n8n läuft.

Ideen für Weiterentwicklung

↑ Zur Kapitelübersicht

Was das Projekt in einer späteren Version können könnte.

Ortsname aus GPS-Koordinaten

Die EXIF-Daten enthalten oft GPS-Koordinaten. Eine Reverse-Geocoding-API (z.B. die kostenlose Nominatim-API von OpenStreetMap) wandelt Koordinaten in einen Ortsnamen um – 53.5872, 10.0082hamburg-winterhude. Das macht Dateinamen noch aussagekräftiger.

Qualitätsbewertung

Claude kann das Bild auch nach technischer Qualität bewerten – scharf, unscharf, Gegenlicht. So könnten offensichtliche Ausschüsse in einen separaten Ordner verschoben werden, statt ins Hauptarchiv zu kommen.

Mehrsprachige Beschreibungen

Den Prompt auf Englisch umstellen für eine internationale Dateinamens-Konvention – hilfreich wenn die Fotos später in Tools wie Lightroom oder Capture One verwaltet werden, die englische Stichwörter bevorzugen.

Zusammenfassung nach dem Import

Nach jeder Import-Session eine Telegram-Nachricht senden: „17 Fotos verarbeitet, 3 in Hamburg, 14 in Cuxhaven." Dafür einen zweiten Zeitplan-Workflow, der die Import-Logs des Tages zusammenfasst.