Zum Inhalt

Dateien (fs)

Mit fs liest und schreibt ein Skript Dateien. Alles passiert in einem geschützten Ordner auf dem Server, der Sandbox. Ein Skript kommt nicht aus ihr heraus — es kann also keine Systemdateien lesen oder überschreiben.

Pfade beginnen immer mit / und gelten innerhalb der Sandbox: fs["/berichte/april.txt"]. Was ein Skript dort ablegt, sieht der Betrieb im Sandbox-Ordner auf dem Server und kann es mit anderen Programmen abholen.

Ausführliches dazu: fs — Dateien in der Sandbox. Die Beispiele arbeiten im Übungsordner /beispiele/dateien; das letzte Beispiel räumt ihn auf.

Einsteiger

1. Eine Textdatei schreiben

writeText legt die Datei an (samt fehlender Ordner) oder überschreibt sie.

fs["/beispiele/dateien/hallo.txt"].writeText("Hallo aus KCForms!\n");
log.info("Datei geschrieben");

2. Eine Textdatei lesen

var inhalt = fs["/beispiele/dateien/hallo.txt"].readText();
log.info("Inhalt: " + inhalt);

3. Prüfen, ob eine Datei existiert

fs["/pfad"] liefert immer ein Datei-Objekt — ob es die Datei wirklich gibt, sagt exists.

var f = fs["/beispiele/dateien/gibt-es-nicht.txt"];
if (f.exists) {
  log.info("Datei vorhanden");
} else {
  log.info("Datei fehlt: " + f.path);
}

4. Name und Größe anzeigen

var f = fs["/beispiele/dateien/hallo.txt"];
log.info("Name: " + f.name);
log.info("Größe: " + f.size + " Byte");
log.info("Liegt in: " + f.parent.path);

size nur bei vorhandenen Dateien abfragen — sonst gibt es einen Fehler.

5. Kurz und knapp mit text

Die Eigenschaft text liest und schreibt wie readText/writeText.

fs["/beispiele/dateien/notiz.txt"].text = "Notiz vom " + new Date().format("dd.MM.yyyy");
log.info(fs["/beispiele/dateien/notiz.txt"].text);

6. Einen Ordner anlegen

mkdir(true) legt auch fehlende Zwischenordner an.

var ordner = fs["/beispiele/dateien/2026/april"];
ordner.mkdir(true);
log.info(ordner.directory ? "Ordner da" : "kein Ordner");

7. Den Inhalt eines Ordners auflisten

list() liefert alle Einträge eines Ordners.

fs["/beispiele/dateien"].list().forEach(function (e) {
  log.info((e.directory ? "[Ordner] " : "") + e.name);
});

8. Eine Datei löschen

remove() liefert true, wenn die Datei danach weg ist.

var f = fs["/beispiele/dateien/notiz.txt"];
if (f.remove()) log.info("Datei gelöscht");

Fortgeschritten

9. Eine Zeile an eine Datei anhängen

Lesen, Zeile anhängen, zurückschreiben — ein einfaches Protokoll.

var f = fs["/beispiele/dateien/protokoll.txt"];
var bisher = f.exists ? f.readText() : "";
var zeile = new Date().format("dd.MM.yyyy HH:mm:ss") + " " + ctx.user.name + " war hier\n";
f.writeText(bisher + zeile);
log.info(f.readText().split("\n").filter(Boolean).length + " Zeilen im Protokoll");

10. Nur bestimmte Dateien auflisten

list nimmt eine Filter-Funktion: nur Einträge, für die sie true sagt.

var txt = fs["/beispiele/dateien"].list(function (e) {
  return e.file && e.name.endsWith(".txt");
});
log.info("Textdateien: " + txt.map(function (e) { return e.name; }).join(", "));

11. Eine Datei kopieren

fs["/beispiele/dateien/hallo.txt"].copy("/beispiele/dateien/sicherung/hallo-kopie.txt");
if (fs["/beispiele/dateien/sicherung/hallo-kopie.txt"].exists) log.info("Kopie vorhanden");

12. Umbenennen oder verschieben

rename bekommt den neuen Pfad — in einem anderen Ordner heißt das „verschieben".

var f = fs["/beispiele/dateien/sicherung/hallo-kopie.txt"];
f.rename("/beispiele/dateien/archiv/hallo-alt.txt");
log.info(fs["/beispiele/dateien/archiv/hallo-alt.txt"].exists ? "verschoben" : "nicht gefunden");

13. Eindeutige Dateinamen

Schreiben mehrere Benutzer gleichzeitig, sollte jede Datei einen eigenen Namen haben — etwa mit Zeitstempel und Benutzer.

var name = "export-" + new Date().format("yyyyMMdd-HHmmss") + "-" + ctx.user.name + ".txt";
var f = fs["/beispiele/dateien/exporte/" + name];
f.writeText("Export von " + ctx.user.name);
log.info("Geschrieben: " + f.path);

14. Umlaute und Zeichensätze

Standard ist UTF-8. Für ältere Programme kannst du einen anderen Zeichensatz angeben.

var f = fs["/beispiele/dateien/windows.txt"];
f.writeText("Grüße aus Köln", "windows-1252");
log.info("richtig gelesen: " + f.readText("windows-1252"));
log.info("Bytes: " + f.size);   // 14 statt 17 – ein Byte pro Umlaut

15. Daten als JSON speichern und wieder laden

JSON.stringify macht aus einem Objekt Text, JSON.parse macht daraus wieder ein Objekt.

var einstellungen = { farbe: "blau", schrift: 12, zuletzt: new Date().format("yyyy-MM-dd") };
fs["/beispiele/dateien/einstellungen.json"].writeText(JSON.stringify(einstellungen, null, 2));

var geladen = JSON.parse(fs["/beispiele/dateien/einstellungen.json"].readText());
log.info("Farbe: " + geladen.farbe + ", Schrift: " + geladen.schrift);

16. Mit einem Ordner-Objekt arbeiten

ordner["/datei"] ist dasselbe wie der volle Pfad — praktisch, wenn du mehrere Dateien im selben Ordner brauchst.

var ordner = fs["/beispiele/dateien/tage"];
ordner.mkdir(true);   // der Ordner muss existieren, bevor du Unterpfade nutzt
["montag", "dienstag", "mittwoch"].forEach(function (tag) {
  ordner["/" + tag + ".txt"].writeText("Plan für " + tag);
});
log.info(ordner.list().length + " Dateien in " + ordner.path);

17. Eine Datei als Bytes kopieren

Bilder, PDFs und andere Nicht-Text-Dateien liest du mit readBytes und schreibst sie mit writeBytes.

var quelle = fs["/beispiele/dateien/hallo.txt"];
var ziel = fs["/beispiele/dateien/bytes-kopie.bin"];
ziel.writeBytes(quelle.readBytes());
log.info(ziel.size === quelle.size ? "gleich groß" : "unterschiedlich");

Profi

18. Alle Dateien eines Ordnerbaums finden

Eine Funktion, die sich für jeden Unterordner selbst aufruft („Rekursion").

function alleDateien(ordner, ergebnis) {
  ordner.list().forEach(function (e) {
    if (e.directory) alleDateien(e, ergebnis);
    else ergebnis.push(e);
  });
  return ergebnis;
}

var dateien = alleDateien(fs["/beispiele/dateien"], []);
dateien.forEach(function (d) { log.info(d.path + " (" + d.size + " Byte)"); });
log.info(dateien.length + " Dateien insgesamt");

19. Wie viel Platz belegt ein Ordner?

function groesse(ordner) {
  var summe = 0;
  ordner.list().forEach(function (e) {
    summe += e.directory ? groesse(e) : e.size;
  });
  return summe;
}

var bytes = groesse(fs["/beispiele/dateien"]);
log.info("Der Übungsordner belegt " + (bytes / 1024).toFixed(1) + " KB");

20. Eingangsordner abarbeiten

Ein typischer Ablauf mit anderen Programmen: Dateien liegen in /eingang, das Skript verarbeitet jede und verschiebt sie nach /erledigt — oder nach /fehler, wenn etwas nicht stimmt.

var basis = "/beispiele/dateien/import";
// Übungsdaten bereitlegen
fs[basis + "/eingang/a.txt"].writeText("Menge=5");
fs[basis + "/eingang/b.txt"].writeText("Menge=7");
fs[basis + "/eingang/c.txt"].writeText("kaputt");

var ok = 0, kaputt = 0;
fs[basis + "/eingang"].list(function (e) { return e.file; }).forEach(function (datei) {
  var name = datei.name;
  try {
    var treffer = /^Menge=(\d+)$/.exec(datei.readText().trim());
    if (!treffer) throw new Error("unbekanntes Format");
    log.info(name + ": Menge " + treffer[1]);
    datei.rename(basis + "/erledigt/" + name);
    ok++;
  } catch (e) {
    log.warn(name + ": " + e.message);
    datei.rename(basis + "/fehler/" + name);
    kaputt++;
  }
});
log.info("verarbeitet: " + ok + ", Fehler: " + kaputt);

21. Einstellungen mit Standardwerten laden

Fehlt die Datei oder ein Wert darin, gelten Standardwerte. So läuft das Skript auch beim allerersten Mal.

function ladeEinstellungen(pfad, standard) {
  var f = fs[pfad];
  var gelesen = {};
  if (f.exists) {
    try { gelesen = JSON.parse(f.readText()); }
    catch (e) { log.warn("Einstellungen unlesbar, nehme Standard: " + e.message); }
  }
  var ergebnis = {};
  for (var k in standard) ergebnis[k] = (gelesen[k] !== undefined) ? gelesen[k] : standard[k];
  return ergebnis;
}

var cfg = ladeEinstellungen("/beispiele/dateien/config.json", { grenze: 500, empfaenger: "team@beispiel.example" });
log.info("Grenze " + cfg.grenze + ", Empfänger " + cfg.empfaenger);

22. Eine Datei aus der Sandbox verschicken

Eine Datei als Anhang an eine E-Mail (siehe auch E-Mail).

var f = fs["/beispiele/dateien/hallo.txt"];
var mp = new mail.MessageMultipart();
mp.addText("Im Anhang die gewünschte Datei.");
mp.addResource(f, "hallo.txt", "text/plain", null);

var msg = new mail.Message();
msg.to = "empfaenger@beispiel.example";
msg.subject = "Datei " + f.name;
msg.content = mp;
mail.send(msg);

23. Eine Sandbox-Datei zum Download anbieten

var f = fs["/beispiele/dateien/hallo.txt"];
var url = delivery.deliver(f.readBytes(), { name: f.name, mime: "text/plain" });
view.add(new view.Link(url, f.name + " herunterladen"));
log.info("Link: " + url);

24. Aufräumen

var ordner = fs["/beispiele/dateien"];
if (ordner.exists) ordner.remove(true);
log.info("Übungsordner aufgeräumt");

remove(true) löscht ohne Rückfrage

Alles unterhalb des Ordners ist danach weg. Prüfe den Pfad genau, bevor du so ein Skript laufen lässt.