Worum es geht
Der Inhalt jeder Datei liegt als eigene Datei im Dateispeicher, meist einem Verzeichnis auf einem Volume. Der Pfad dorthin ist fest aufgebaut. Aus ihm ergibt sich auch die Trennung der Mandanten: Jeder Mandant hat sein eigenes Unterverzeichnis.
Der Pfad
<basePath>/[<mandant>/]<modellpfad>/<fileId>/files/t1/fileasset/910e2c19-d5d1-4dae-8229-ea5fcbcc6194| Teil | woher | Beispiel |
|---|---|---|
basePath | Konfiguration codamai.cdms.persistence.file.basePath | /files/ |
| Mandant | Mandant der Anfrage, nur wenn nötig (siehe unten) | t1 |
| Modellpfad | aus dem API-Pfad des Modells, beim Generieren festgelegt | fileasset/, company/logo/ |
fileId | UUID, die der Server beim ersten Speichern vergibt | 910e2c19-… |
Kein Teil des Pfads kommt direkt vom Benutzer. Der Anzeigename Vertrag.pdf steht nur im Datensatz. Den Mandanten prüft CDMS, bevor er in den Pfad kommt: Erlaubt sind Buchstaben, Ziffern, ., _ und -, aber kein ...
Wann der Mandant im Pfad steht
| Betriebsart | Ebene des Modells | Pfad |
|---|---|---|
| MULTI | Mandant oder Benutzer | <basePath>/<mandant>/<modell>/<fileId> |
| MULTI | System | <basePath>/<modell>/<fileId>, gemeinsam für alle Mandanten |
| SINGLE | alle | <basePath>/<modell>/<fileId>, ein gemeinsames Verzeichnis |
Die Ebene Benutzer trennt im Speicher nicht weiter als die Ebene Mandant. Wer welche Datei eines Mandanten sehen darf, entscheiden die Rechte und der Owner-Filter beim Lesen des Datensatzes. Siehe Modell-Ebenen: System, Mandant, Benutzer.
Ein Verzeichnisbaum
flowchart TB
B["/files/"] --> T1["t1/"]
B --> T2["t2/"]
B --> S["country-flags/ (System-Modell)"]
T1 --> T1F["fileasset/"]
T1F --> F1["910e2c19-… (aktuell)"]
T1F --> F1V["910e2c19-….1790000100000 (Version)"]
T1 --> T1L["company/logo/"]
T1L --> L1["3b7a…"]
T2 --> T2F["fileasset/"]
T2F --> F2["5c01…"]
S --> S1["a9e2…"]
Mandantentrennung
Die Trennung liegt allein im Pfad. Eine Anfrage von Mandant t2 sucht nur unter /files/t2/. Selbst mit der fileId einer Datei von t1 findet sie dort nichts.
| Situation | Ergebnis |
|---|---|
| t1 schreibt, t1 liest | gefunden |
t1 schreibt, t2 liest dieselbe fileId | nicht gefunden, anderes Verzeichnis |
| Mandanten-Modell, Anfrage ohne Mandant | abgelehnt, CDMS_FILE_TENANT_REQUIRED |
| Mandant mit unzulässigen Zeichen | abgelehnt, CDMS_FILE_TENANT_INVALID |
| System-Modell, beliebiger Mandant | gefunden, gemeinsames Verzeichnis |
Vorher hat schon die Datenbank getrennt: Den Datensatz einer fremden Datei findet eine Anfrage gar nicht erst. Siehe Mandantentrennung in CDMS.
Atomar ablegen
Ein Upload kommt zuerst als Zwischendatei an, oft in einem anderen Dateisystem als der Speicher. Würde CDMS ihn direkt an den Zielplatz kopieren, könnte ein gleichzeitiger Download eine halb geschriebene Datei bekommen. Deshalb:
-
1CDMS→File storagebei auditierten Modellen: benennt den bisherigen Inhalt in eine Version um
-
2CDMS→File storageschreibt den Inhalt unter einen privaten Namen im Zielverzeichnis:
<fileId>.staging-<zufall>Das kann bei großen Dateien dauern. Niemand liest diesen Namen. -
3CDMS→File storagebenennt die Zwischendatei in einem Schritt auf
<fileId>umErgebnis: Unter<fileId>liegt nie ein halb geschriebener Inhalt.
Verzeichnisrechte
Neue Verzeichnisse legt CDMS mit den Rechten rwxr-x--- an: Der Anwendungsbenutzer darf alles, seine Gruppe lesen, alle anderen nichts. So bleiben Mandantendaten auch für andere Prozesse auf demselben Server verschlossen. Eine Sicherung, die unter derselben Gruppe läuft, kann lesen. Das Basisverzeichnis selbst legt CDMS nicht an, siehe Speicher-Backends.
Fallen
Wie es weitergeht
- Wo der Speicher herkommt: Speicher-Backends
- Alte Stände neben dem aktuellen: Dateiversionen
- Wie ein Upload ankommt: Hochladen