> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langdock.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dateiunterstützung in benutzerdefinierten Integrationen

> Verarbeite Dateieingaben und -ausgaben in benutzerdefinierten Aktionen und Triggern.

## 1. Dateieingabe in Aktionen

Dateieingaben ermöglichen es Nutzern, Dateien hochzuladen, die deine Aktion dann verarbeiten oder an externe APIs senden kann.

### 1.1 Wann du Dateieingaben verwenden solltest

* **Dateien zu externen Tools hochladen**: Sende von Nutzern hochgeladene Dokumente an APIs, Cloud-Speicher oder externe Dienste wie E-Mail oder Ticket-Systeme.
* **Nutzerdateien verarbeiten**: Analysiere, konvertiere oder transformiere von Nutzern hochgeladene Dateien

### 1.2 Dateieingabefelder hinzufügen

#### Einzelne Dateieingabe

Füge ein Eingabefeld vom Typ "FILE" hinzu, um eine Datei zu akzeptieren:

<img src="https://mintcdn.com/langdock-34/Edse5IEKUoKtw9hW/images/image.png?fit=max&auto=format&n=Edse5IEKUoKtw9hW&q=85&s=324f23ec7460b0300065405d4bcb6ea8" alt="image.png" width="2706" height="1828" data-path="images/image.png" />

#### Mehrere Dateieingaben

Für mehrere Dateien aktiviere *Mehrere Dateien erlauben*:

### 1.3 Auf Dateidaten im Code zugreifen

Jede hochgeladene Datei wird als **`FileData`**-Objekt mit diesem exakten Format geliefert:

```typescript theme={null}
{
  fileName: "Rechnung.pdf",
  mimeType: "application/pdf",
  size: 102400,                    // Bytes
  base64: "JVBERi0xLjQK...",        // Binärinhalt, Base64-kodiert
  lastModified: "2024-01-15T10:30:00Z"  // ISO-Datumsstring
}
```

> **Wichtig**: Dateieingaben enthalten KEINE `text`-Eigenschaft. Die `text`-Abkürzung existiert nur für Dateiausgaben.

#### Zugriff auf einzelne Datei

```javascript theme={null}
// 'document' ist in diesem Beispiel die ID des erstellten Eingabefelds
const document = data.input.document; // FileData-Objekt
const buffer = Buffer.from(document.base64, "base64");
ld.log(`Verarbeite ${document.fileName} (${document.size} Bytes)`);
```

#### Zugriff auf mehrere Dateien

```javascript theme={null}
// 'attachments' ist in diesem Beispiel die ID des erstellten Eingabefelds
const files = data.input.attachments; // FileData[] Array
for (const file of files) {
  const buffer = Buffer.from(file.base64, "base64");
  await processFile(buffer, file.mimeType);
}
```

> **Datenstruktur**: Wenn "Mehrere Dateien erlauben" aktiviert ist, ist `data.input.fieldName` ein Array. Andernfalls ist es ein einzelnes Objekt.

### 1.4 Häufige Eingabemuster

<Expandable title="E-Mail mit Anhängen">
  ```javascript theme={null}
  // Gmail-artige E-Mail mit Dateianhängen
  const recipient = data.input.mailRecipient;
  const subject = data.input.mailSubject;
  const body = data.input.mailBody;
  const attachments = data.input.attachments || [];

  // Multipart-E-Mail erstellen
  let email = `To: ${recipient}\r\nSubject: ${subject}\r\n`;

  if (attachments.length > 0) {
  const boundary = `boundary_${Date.now()}`;
  email += `Content-Type: multipart/mixed; boundary="${boundary}"\r\n\r\n`;
  email += `--${boundary}\r\nContent-Type: text/html\r\n\r\n${body}\r\n`;

    // Jeden Anhang hinzufügen
    for (const attachment of attachments) {
      email += `--${boundary}\r\n`;
      email += `Content-Type: ${attachment.mimeType}\r\n`;
      email += `Content-Transfer-Encoding: base64\r\n`;
      email += `Content-Disposition: attachment; filename="${attachment.fileName}"\r\n`;
      email += `\r\n${attachment.base64}\r\n`;
    }
    email += `--${boundary}--`;

  }

  ```
</Expandable>

<Expandable title="Dokument-Upload zu API">
  ```javascript theme={null}
  const document = data.input.document;

  // Dateityp validieren
  if (!document.mimeType.startsWith('application/pdf')) {
    throw new Error('Nur PDF-Dateien werden unterstützt');
  }

  try {
    const response = await ld.request({
      method: 'POST',
      url: 'https://api.example.com/documents',
      headers: {
        'Authorization': `Bearer ${data.auth.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: {
        filename: document.fileName,
        content: document.base64,
        mimeType: document.mimeType
      }
    });

    return {
      success: true,
      documentId: response.json.id,
      message: `${document.fileName} erfolgreich hochgeladen`
    };
  } catch (error) {
    return {
      success: false,
      error: `Fehler beim Hochladen von ${document.fileName}: ${error.message}`
    };
  }
  ```
</Expandable>

<Expandable title="Batch-Dateiverarbeitung">
  ```javascript theme={null}
  const files = data.input.files || [];

  if (files.length === 0) {
    return { error: 'Keine Dateien bereitgestellt' };
  }

  const results = [];

  for (const file of files) {
    try {
      const buffer = Buffer.from(file.base64, 'base64');
      
      // Basierend auf Dateityp verarbeiten
      let result;
      if (file.mimeType.startsWith('image/')) {
        result = await processImage(buffer);
      } else if (file.mimeType === 'application/pdf') {
        result = await processPDF(buffer);
      } else {
        throw new Error(`Nicht unterstützter Dateityp: ${file.mimeType}`);
      }
      
      results.push({
        filename: file.fileName,
        status: 'success',
        result: result
      });
      
      ld.log(`${file.fileName} erfolgreich verarbeitet`);
    } catch (error) {
      results.push({
        filename: file.fileName,
        status: 'error',
        error: error.message
      });
      
      ld.log(`Fehler beim Verarbeiten von ${file.fileName}: ${error.message}`);
    }
  }

  return {
    success: true,
    processed: results.filter(r => r.status === 'success').length,
    failed: results.filter(r => r.status === 'error').length,
    results: results
  };
  ```
</Expandable>

### 1.5 Eingabevalidierung & Fehlerbehandlung

```javascript theme={null}
// Dateipräsenz validieren
const files = data.input.attachments;
if (!files || files.length === 0) {
  return { error: 'Keine Dateien bereitgestellt. Bitte hänge mindestens eine Datei an.' };
}

// Dateitypen validieren
const allowedTypes = ['application/pdf', 'image/jpeg', 'image/png'];
for (const file of files) {
  if (!allowedTypes.includes(file.mimeType)) {
    return {
      error: `Nicht unterstützter Dateityp: ${file.mimeType}. Erlaubt: ${allowedTypes.join(', ')}`
    };
  }
}

// Dateimetadaten für Debugging protokollieren
ld.log('Verarbeite Dateien:', files.map(f => ({
  fileName: f.fileName,
  mimeType: f.mimeType,
  size: f.size
})));
```

## 2. Dateiausgabe in Aktionen

Dateiausgaben ermöglichen es deiner Aktion, Dateien zu generieren und zurückzugeben, die Nutzer herunterladen oder in nachfolgenden Aktionen verwenden können.

### 2.1 Wann du Dateiausgaben verwenden solltest

* **Berichte generieren**: PDFs, Tabellenkalkulationen oder Dokumente aus Daten erstellen
* **Dateien von APIs abrufen**: Dateien von externen Diensten herunterladen
* **Dateien transformieren**: Zwischen Formaten konvertieren oder hochgeladene Dateien verarbeiten
* **Daten exportieren**: CSV-Exporte, Backup-Dateien oder Daten-Dumps erstellen

### 2.2 Dateiausgabeformat

Gib Dateien unter einem `files`-Schlüssel in deiner Antwort zurück:

```javascript theme={null}
// Einzelne Dateiausgabe
return {
  files: {
    fileName: "bericht.pdf",
    mimeType: "application/pdf",
    base64: "JVBERi0xLjQK..."  // Base64-kodierter Inhalt
  }
};

// Mehrere Dateiausgaben
return {
  files: [
    {
      fileName: "daten.csv",
      mimeType: "text/csv",
      text: "Name,Email\nJohn,john@example.com"  // Text-Abkürzung für UTF-8
    },
    {
      fileName: "diagramm.png",
      mimeType: "image/png",
      base64: "iVBORw0KGgoAAAANSUhEUgAA..."
    }
  ]
};
```

#### Dateiausgabe-Eigenschaften

| Feld           | Erforderlich | Hinweise                                         |
| -------------- | ------------ | ------------------------------------------------ |
| `fileName`     | ✓            | Richtige Dateierweiterung einschließen           |
| `mimeType`     | ✓            | Genauer MIME-Typ für ordnungsgemäße Verarbeitung |
| `base64`       | ✓\*          | Base64-kodierter Binärinhalt                     |
| `text`         | ✓\*          | UTF-8 Textinhalt (Alternative zu base64)         |
| `lastModified` | –            | ISO-Datumsstring (Standard ist aktuelle Zeit)    |

\*Gib entweder `base64` ODER `text` an, niemals beides.

### 2.3 Häufige Ausgabemuster

<Expandable title="PDF-Bericht generieren">
  ```javascript theme={null}
  // PDF-Bericht aus Daten generieren
  const reportData = await fetchReportData();

  // PDF-Inhalt erstellen (mit benutzerdefinierter Funktion / Endpunkt)
  const pdfBuffer = await generatePDF({
    title: 'Monatsbericht',
    data: reportData,
    template: 'standard'
  });

  return {
    files: {
      fileName: `monatsbericht-${new Date().toISOString().slice(0,7)}.pdf`,
      mimeType: 'application/pdf',
      base64: pdfBuffer.toString('base64')
    },
    success: true,
    message: `Bericht mit ${reportData.length} Einträgen generiert`
  };
  ```
</Expandable>

<Expandable title="CSV-Datenexport">
  ```javascript theme={null}
  // Daten als CSV mit Text-Abkürzung exportieren
  const customers = await fetchCustomers();

  // CSV-Inhalt erstellen
  const csvHeader = 'Name,Email,Erstellt,Status';
  const csvRows = customers.map(c => 
    `"${c.name}","${c.email}","${c.created}","${c.status}"`
  );
  const csvContent = [csvHeader, ...csvRows].join('\n');

  return {
    files: {
      fileName: `kunden-export-${new Date().toISOString().slice(0,10)}.csv`,
      mimeType: 'text/csv',
      text: csvContent  // Text für UTF-8-Inhalt verwenden
    },
    success: true,
    exported: customers.length
  };
  ```
</Expandable>

<Expandable title="Datei von API herunterladen">
  ```javascript theme={null}
  // Datei von externer API herunterladen
  const fileId = data.input.fileId;

  try {
    const response = await ld.request({
      method: 'GET',
      url: `https://api.example.com/files/${fileId}`,
      headers: {
        'Authorization': `Bearer ${data.auth.apiKey}`
      },
      responseType: 'stream'
    });

    // Dateinamen aus Response-Headers oder API erhalten
    const filename = response.headers['content-disposition']
      ?.match(/filename="(.+)"/)?.[1] || `datei-${fileId}`;

    return {
      files: {
        fileName: filename,
        mimeType: response.headers['content-type'] || 'application/octet-stream',
        base64: Buffer.from(response.buffer).toString('base64')
      },
      success: true,
      message: `${filename} heruntergeladen`
    };
  } catch (error) {
    return {
      success: false,
      error: `Fehler beim Herunterladen der Datei: ${error.message}`
    };
  }
  ```
</Expandable>

<Expandable title="Mehrere Dateien verarbeiten und zurückgeben">
  ```javascript theme={null}
  // Eingabedateien verarbeiten und modifizierte Versionen zurückgeben
  const inputFiles = data.input.documents || [];
  const outputFiles = [];

  for (const file of inputFiles) {
    try {
      const buffer = Buffer.from(file.base64, 'base64');
      
      // Datei verarbeiten (z.B. komprimieren, konvertieren, etc.)
      const processedBuffer = await processFile(buffer, file.mimeType);
      
      outputFiles.push({
        fileName: `verarbeitet-${file.fileName}`,
        mimeType: file.mimeType,
        base64: processedBuffer.toString('base64')
      });
    } catch (error) {
      ld.log(`Fehler beim Verarbeiten von ${file.fileName}: ${error.message}`);
    }
  }

  return {
    files: outputFiles,
    success: true,
    processed: outputFiles.length,
    message: `${outputFiles.length} von ${inputFiles.length} Dateien verarbeitet`
  };
  ```
</Expandable>

## 3. Dateiausgabe in Triggern

Trigger können auch Dateien zurückgeben, wenn sie Ereignisse erkennen, die Dateianhänge enthalten, oder wenn sie Dateien basierend auf Trigger-Daten generieren.

> **Wichtiger Unterschied**: Im Gegensatz zu Aktionen, die Objekte direkt zurückgeben, müssen Trigger ein **Array** von Ereignissen zurückgeben. Jedes Ereignis benötigt `id`, `timestamp` und `data` Eigenschaften, mit Dateien innerhalb des `data`-Objekts.

### 3.1 Wann du Dateiausgaben in Triggern verwenden solltest

* **E-Mail-Anhänge**: Anhänge von eingehenden E-Mails weiterleiten
* **Dateiänderungsereignisse**: Modifizierte oder neue Dateien von überwachten Systemen zurückgeben
* **Generierte Benachrichtigungen**: Zusammenfassungsdateien oder Berichte erstellen, wenn Ereignisse auftreten
* **API-Webhook-Dateien**: Dateien aus Webhook-Payloads verarbeiten und weiterleiten

### 3.2 Trigger-Dateiausgabeformat

Trigger müssen ein Array von Objekten mit `id`, `timestamp` und `data` Eigenschaften zurückgeben. Dateien kommen **innerhalb** des `data`-Objekts:

```javascript theme={null}
// Erforderliches Trigger-Format mit Dateien
return [
  {
    id: "msg_123",                    // Eindeutiger Bezeichner für dieses Ereignis
    timestamp: "2024-01-15T10:30:00Z", // Ereignis-Zeitstempel
    data: {
      from: "sender@example.com",
      subject: "Rechnung #12345",
      body: "Bitte finde die Rechnung im Anhang.",
      messageId: "msg_123",
      files: [                        // Dateien kommen INNERHALB des data-Objekts
        {
          fileName: "rechnung-12345.pdf",
          mimeType: "application/pdf",
          base64: "JVBERi0xLjQK..."
        }
      ]
    }
  }
];
```

> **Wichtig**: Im Gegensatz zu Aktionen müssen Trigger ein Array von Ereignissen zurückgeben, und `files` muss innerhalb des `data`-Objekts sein, nicht auf oberster Ebene.

### 3.3 Häufige Trigger-Muster

<Expandable title="E-Mail-Trigger mit Anhängen">
  ```javascript theme={null}
  // Gmail-Trigger verarbeitet eingehende E-Mails
  const emails = await fetchNewEmails();
  const results = [];

  for (const email of emails) {
    const attachments = [];
    
    // E-Mail-Anhänge verarbeiten, falls vorhanden
    if (email.attachments && email.attachments.length > 0) {
      for (const attachment of email.attachments) {
        // Anhanginhalt herunterladen
        const content = await downloadAttachment(attachment.id);
        
        attachments.push({
          fileName: attachment.filename,
          mimeType: attachment.mimeType,
          base64: content.toString('base64')
        });
      }
    }
    
    results.push({
      id: email.id,                     // Erforderlich: eindeutige Ereignis-ID
      timestamp: email.date,            // Erforderlich: Ereignis-Zeitstempel
      data: {
        from: email.from,
        subject: email.subject,
        body: email.body,
        receivedAt: email.date,
        messageId: email.id,
        threadId: email.threadId,
        files: attachments              // Dateien innerhalb des data-Objekts
      }
    });
  }

  return results;  // Array von Trigger-Ereignissen zurückgeben
  ```
</Expandable>

<Expandable title="Dateisystem-Monitor">
  ```javascript theme={null}
  // Auf neue Dateien in einem Verzeichnis überwachen
  const newFiles = await checkForNewFiles(data.input.directory);
  const results = [];

  for (const file of newFiles) {
    try {
      // Dateiinhalt herunterladen
      const content = await downloadFile(file.path);
      
      results.push({
        id: file.id || file.path,         // Erforderlich: eindeutiger Dateibezeichner
        timestamp: file.lastModified,     // Erforderlich: Ereignis-Zeitstempel
        data: {
          filePath: file.path,
          fileName: file.name,
          size: file.size,
          modifiedAt: file.lastModified,
          event: 'file_created',
          files: [                        // Dateien-Array innerhalb des data-Objekts
            {
              fileName: file.name,
              mimeType: file.mimeType || 'application/octet-stream',
              base64: content.toString('base64')
            }
          ]
        }
      });
    } catch (error) {
      ld.log(`Fehler beim Verarbeiten der Datei ${file.name}: ${error.message}`);
    }
  }

  return results;  // Array von Trigger-Ereignissen zurückgeben
  ```
</Expandable>

## 4. Plattform-Einschränkungen & Limits

| Einschränkung                     | Limit          |
| :-------------------------------- | :------------- |
| **Gesamte Dateigröße pro Aktion** | **100 MB**     |
| **Maximale Dateien pro Aktion**   | **20 Dateien** |
| Einzelne Dokumente                | ≤ 256 MB\*     |
| Einzelne Bilder                   | ≤ 20 MB        |
| Einzelne Tabellenkalkulationen    | ≤ 30 MB        |
| Einzelne Audiodateien             | ≤ 200 MB\*     |
| Einzelne Videodateien             | ≤ 20 MB        |
| Andere Dateitypen                 | ≤ 256 MB\*     |
| **Aktions-Ausführungs-Timeout**   | **2 Minuten**  |

\*Immer noch durch das 100 MB Gesamtlimit begrenzt.

> **Validierung**: Das Überschreiten von Limits löst einen Fehler aus **bevor dein Code ausgeführt wird**.

```json theme={null}
{
  "error": "Gesamte Dateigröße (120.0 MB) überschreitet das Aktions-Ausführungslimit von 100.0 MB. Bitte verwende kleinere Dateien oder reduziere die Anzahl der Dateien."
}
```

### 4.1 Sandbox-Bibliotheksbeschränkungen

> Nutzerdefinierter Aktions- und Trigger-Code läuft in einer sicheren Sandbox-Umgebung. **Du kannst keine externen Bibliotheken installieren oder importieren (npm, pip, etc.) – nur eine begrenzte Anzahl von eingebauten JavaScript/Node.js APIs ist verfügbar.** Für erweiterte Dateiverarbeitung (z.B. PDF-Parsing, Bildmanipulation), verwende externe APIs oder Dienste und rufe sie aus deinem Code auf.

<Info>
  Für Datenformat-Konvertierungen (CSV ↔ Parquet ↔ Arrow, JSON → CSV) siehe [Sandbox-Utilities](/de/using-langdock/guides/integrations/sandbox-utilities#datenformat-konvertierungen) für integrierte Konvertierungsfunktionen.
</Info>

## 5. Best Practices

### Best Practices für Dateieingaben

* **Dateitypen früh validieren**: Prüfe MIME-Typen vor der Verarbeitung
* **Fehlende Dateien elegant behandeln**: Verwende `|| []` für optionale Datei-Arrays
* **Dateimetadaten protokollieren**: Hilft beim Debuggen von Problemen ohne Inhalt preiszugeben
* **Klare Fehlermeldungen bereitstellen**: Sage Nutzern genau, was schiefgelaufen ist

```javascript theme={null}
// Gutes Validierungsmuster
const files = data.input.attachments || [];
if (files.length === 0) {
  return { error: 'Bitte hänge mindestens eine Datei zur Verarbeitung an.' };
}

const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
for (const file of files) {
  if (!allowedTypes.includes(file.mimeType)) {
    return {
      error: `Datei "${file.fileName}" hat nicht unterstützten Typ ${file.mimeType}. Erlaubt: ${allowedTypes.join(', ')}`
    };
  }
}
```

### Best Practices für Dateiausgaben

* **Aussagekräftige Dateinamen verwenden**: Füge Daten, IDs oder beschreibende Namen hinzu
* **Genaue MIME-Typen setzen**: Ermöglicht ordnungsgemäße Dateiverarbeitung und Vorschauen
* **Text-Abkürzung für UTF-8 verwenden**: Effizienter als base64 für Textdateien
* **Verarbeitungsstatus einschließen**: Hilf Nutzern zu verstehen, was passiert ist

```javascript theme={null}
// Gutes Ausgabemuster
return {
  files: {
    fileName: `kundenbericht-${new Date().toISOString().slice(0,10)}.pdf`,
    mimeType: 'application/pdf',
    base64: reportBuffer.toString('base64')
  },
  success: true,
  recordsProcessed: customerData.length,
  message: `Bericht mit ${customerData.length} Kundendatensätzen generiert`
};
```

### Performance Best Practices

* **Dateien nach Möglichkeit parallel verarbeiten**: Verwende `Promise.all()` für unabhängige Vorgänge
* **Vermeide alle Dateien in den Speicher zu laden**: Verarbeite eine nach der anderen bei großen Batches
* **Fortschritt protokollieren**: Verwende `ld.log()` um den Verarbeitungsstatus zu verfolgen
* **Fehler elegant behandeln**: Fahre mit der Verarbeitung anderer Dateien fort, wenn eine fehlschlägt

```javascript theme={null}
// Gutes Fehlerbehandlungsmuster
const results = [];
for (const file of files) {
  try {
    const result = await processFile(file);
    results.push({ fileName: file.fileName, status: "success", result });
    ld.log(`✓ ${file.fileName} verarbeitet`);
  } catch (error) {
    results.push({
      fileName: file.fileName,
      status: "error",
      error: error.message,
    });
    ld.log(`✗ ${file.fileName} fehlgeschlagen: ${error.message}`);
  }
}
```

## 6. Fehlerbehebung

| Problem                | Wahrscheinliche Ursache                  | Lösung                                                      |
| ---------------------- | ---------------------------------------- | ----------------------------------------------------------- |
| "Datei nicht gefunden" | Nutzer hat keine Datei angehängt         | Mache Dateieingabe erforderlich oder füge Validierung hinzu |
| Größenlimit-Fehler     | Dateien überschreiten 100 MB gesamt      | Bitte um kleinere oder weniger Dateien                      |
| "Ungültiger Dateityp"  | Falscher MIME-Typ                        | Validiere `file.mimeType` in deinem Code                    |
| Leerer Dateiinhalt     | Base64-Kodierungsproblem                 | Überprüfe, dass `file.base64` gültig ist                    |
| Timeout-Fehler         | Große Dateien oder langsame Verarbeitung | Optimiere Verarbeitung oder reduziere Dateigrößen           |
| Speicherfehler         | Zu viele große Dateien                   | Verarbeite Dateien sequenziell, nicht parallel              |

### Debug-Helfer

```javascript theme={null}
// Sicheres Protokollieren für Dateimetadaten (protokolliert niemals Inhalt)
function logFileInfo(files) {
  const fileInfo = Array.isArray(files) ? files : [files];
  ld.log(
    "Datei-Info:",
    fileInfo.map((f) => ({
      fileName: f.fileName,
      mimeType: f.mimeType,
      size: f.size,
      hasBase64: !!f.base64,
      hasText: !!f.text, // Existiert nur für Ausgaben
    })),
  );
}
```

***

## FAQ

<AccordionGroup>
  <Accordion title="Wann sollte eine benutzerdefinierte Integration Dateien unterstützen?">
    Unterstütze Dateien, wenn die externe Aktion Dokumente oder binäre Assets hochladen, herunterladen, umwandeln oder zurückgeben muss. Wenn die Aktion nur Text oder Metadaten braucht, ist eine normale strukturierte Eingabe meist einfacher.
  </Accordion>

  <Accordion title="Was sollte ich prüfen, wenn die Dateiverarbeitung in einer benutzerdefinierten Aktion fehlschlägt?">
    Prüfe Dateigröße, Dateityp, Kodierung und die Konfiguration des Eingabefeldes. Prüfe außerdem, ob die externe API einen Multipart-Upload, eine URL, Base64-Daten oder ein anderes Format erwartet.
  </Accordion>
</AccordionGroup>
